将数组中的多个不相邻元素重新排序到特定索引的算法

Posted

技术标签:

【中文标题】将数组中的多个不相邻元素重新排序到特定索引的算法【英文标题】:Algorithm to reorder multiple non-adjacent elements in array to specific index 【发布时间】:2012-06-30 09:23:00 【问题描述】:

我一直在努力解决这个问题,但它不起作用。

我正在使用一些专有技术,一次只允许移动一个元素。超级烦。将其视为 list.moveRow(之前,之后)。

想象一个包含 10 个项目的待办事项列表:1, 2, 3, 4, 5, 6, 7, 8, 9, 10

我想做的是在那里重新排序多个项目, 所以说我选择项目2, 6, 7, and 8

然后我拖放到位置4

我们的想法是元素现在应该是有序的:1, 3, 2, 6, 7, 8, 4, 5, 9, 10

有没有简单的方法来做到这一点?我尝试使用额外的变量来跟踪由于移动的元素会抛出索引而导致的偏移量,但它非常混乱。 :(

【问题讨论】:

查看“splice”命令 - 这将允许您将“选定”项的数组插入到数组中的特定位置。 嘿@Daniel,遗憾的是我使用的API 一次只允许移动一个元素。超级烦。将其视为 list.moveRow(之前,之后)。 T_T 您会进行多次移动还是只进行一次拖放? 必须允许多次移动。 @Carlin - 我不知道为什么我认为你在谈论 JS! 【参考方案1】:

这真的取决于语言。

假设您有一个简单的方法来获取每个项目的 isSelected(),最简单和最通用的似乎是构建一个新数组作为列表(即从第一个索引添加到最后一个索引):

按顺序添加未选中的项目,直到插入位置 添加所选项目 添加剩余的未选中项

然后替换旧数组。

在伪代码中:

func(int[] items, bool[] isselected, int insertpos) 
   newitems = new int[items.length]
   i=0
   for (j=0; j<items.length; j++) 
      if j==insertpos 
          for (k=0; k<items.length; k++) 
              if isselected[k] 
                  newitems[i++] = items[k]
              
          
       else if !isselected[j] 
          newitems[i++] = items[j]
      
   
   return newitems

如果你想要高性能和原地修改......那是另一回事......

【讨论】:

添加了伪代码版本。没有进行任何测试,也许我错过了一个索引,但它应该更清楚。【参考方案2】:

坐下来写出来,想出了一个合理的解决方案。这似乎工作正常:

// Reorder list
function reorder(items, index)
        
    // When we move, we have to account for previously moved rows too
    //  Go from bottom-most selected row first to preserve order
    for (var i = items.length - 1, itemOffset = 0, indexOffset = 0; i >= 0; --i) 
    
        // Move
        list.moveRow(items[i] + itemOffset, index + indexOffset);

        // Change offsets based on location
        //  If next row index is after target index, add to row offset
        if (i-1 >= 0 && items[i-1] >= index) 
            itemOffset++;
         else     // If next index is before target, subtract from target
            itemOffset= 0;
            indexOffset--;
        
    

【讨论】:

【参考方案3】:

我希望我能早点找到这个。无论如何,在现代 JS 中,可以(并且希望!)在没有条件的情况下做到这一点,并且有一个优雅的算法可以通过使用占位符来实现。

具体来说,我们可以使用Symbol 作为在有序集中移动的元素的替代。这样一来,索引算法就被消除了(随之而来的还有大量的条件和地方错误恶化的地方)。

基本上是这样工作的:

withGroupRepositioned(["a", "b", "c", "d"], ["a", "b"], 0) // [ 'a', 'b', 'c', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["a", "b"], 1) // [ 'a', 'b', 'c', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["a", "b"], 2) // [ 'a', 'b', 'c', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["a", "b"], 3) // [ 'c', 'a', 'b', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["b", "a"], 2) // [ 'b', 'a', 'c', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["b", "a"], 3) // [ 'c', 'b', 'a', 'd' ]
withGroupRepositioned(["a", "b", "c", "d"], ["a", "c"], 4) // [ 'b', 'd', 'a', 'c' ]

整个函数是:

const PLACEHOLDER = Symbol();
function withGroupRepositioned(inOrderedSet, valuesToReposition, toIndex) 
  // Do not damage the given set
  const cpy = [].concat(inOrderedSet);

  // Step 1. Replace all traveling elements with placeholders, in place.
  valuesToReposition.forEach((v) => cpy[cpy.indexOf(v)] = PLACEHOLDER);

  // Step 2. Reinsert values as a group, where we want them.
  cpy.splice(toIndex, 0, ...valuesToReposition);

  // Step 3. Remove placeholders.
  return cpy.filter((v) => v !== PLACEHOLDER);

如果要进行就地重排,可以删除第一个数组复制操作。

【讨论】:

以上是关于将数组中的多个不相邻元素重新排序到特定索引的算法的主要内容,如果未能解决你的问题,请参考以下文章

排序算法之希尔排序

排序算法

数据结构与算法——计数排序桶排序基数排序

希尔排序

mpi生成随机数组并进行排序

数组排序是啥?