小编典典

将所有奇数定位的元素移到原位的左半边,甚至定位到右半部分

algorithm

给定具有正整数和负整数的数组,请将所有奇数索引元素向左移动,甚至将偶数索引元素向右移动。

问题的困难部分是在维护订单的同时就地进行。

例如

7, 5, 6, 3, 8, 4, 2, 1

输出应为:

5, 3, 4, 1, 7, 6, 8, 2

如果顺序无关紧要,我们可以使用快速排序的partition()算法。

如何在O(N)中做到这一点?


阅读 260

收藏
2020-07-28

共1个答案

小编典典

  1. 获取大小为3 k +1的最大子数组
  2. 从位置1、3、9,…,3 k-1开始,对该子数组的各个部分应用循环引导算法,将元素移动到其在子数组中的适当位置(偶数索引元素在子元素的左侧) -array,在右边是奇数索引-),被替换的元素也应移动到正确的位置,依此类推,直到此过程返回到起始位置为止。本文给出了数论解释,为什么这样选择起始位置会使子数组改组为正确的顺序。
  3. 使用步骤1和2递归处理数组的其余部分。
  4. 现在,我们只需要将重新排序的零件连接在一起。从整个数组末尾的较小子数组开始。要交换半个子数组,请使用反向算法:reverse(reverse(a),reverse(b)); 或者,对于相等大小的子数组一半,使用成对交换。
  5. 现在所有偶数位置的元素都在左侧。为了使它们正确显示,根据需要,将所有i = 0 .. N / 2-1的元素i和i + N / 2交换。

算法就地,时间复杂度为O(N)。

例:

0 1 2 3 4  5 6 7 8 9   10 11 (the original array)
0 1 2 3 4  5 6 7 8 9 # 10 11 (split to sub-arrays)
0 2 4 3 8  1 6 5 7 9 # 10 11 (first cycle leader iteration, starting with 1)
0 2 4 6 8  1 3 5 7 9 # 10 11 (second cycle leader iteration, starting with 3)
0 2 4 6 8  9 7 5 3 1 # 10 11(2nd half of 1st part& 1st half of 2nd part reversed)
0 2 4 6 8 10 1 3 5 7    9 11 (both halves reversed together)

此算法的变体,不需要步骤5:

  • 在步骤1中,获得大小为3 k -1的最大子数组。
  • 在步骤2中,将偶数索引元素移到子数组的右侧,将奇数索引元素移到左侧。将起始位置0、2、8,… 3 k-1 -1用于循环前导算法。

这是另一种O(N log N)就地算法,不需要数论证明:

  1. 将数组重新解释为单元素2 * 2矩阵的序列,并转置这些矩阵。
  2. 将结果重新解释为由2个元素组成的2 * 2矩阵序列,并将它们转置。
  3. 当矩阵大小小于数组大小时继续。
  4. 现在,我们只需要将重新排序的部分连接在一起(与之前的算法完全一样)。
  5. 交换数组左右两半的元素(与先前算法完全相同)。

例:

0  1   2 3   4 5   6 7  (the original array)
[0 2] [1 3] [4 6] [5 7] (first transposition)
[0 2] [4 6] [1 3] [5 7] (second transposition)

这个问题只是就地矩阵转置的特例。

2020-07-28