【问题标题】:sort an array by relative position按相对位置对数组进行排序
【发布时间】:2012-03-12 04:45:50
【问题描述】:

给定一个包含正整数和负整数的数组,实现一个算法,花费 O(n) 时间和 O(1) 空间将所有负整数放在所有正整数前面,并保持相对位置。 例如:{1,7,-5,9,-12,15} -----> {-5,-12,1,7,9,15}

你有什么想法吗?

【问题讨论】:

标签: algorithm position sorting


【解决方案1】:

您要求一个稳定的就地分区函数。

论文Stable Minimum Space Partitioning in Linear Time (1992)声称有这样的算法,但一些other SO questions对其可行性提出了质疑。

【讨论】:

  • 论文引用:“此外,我们假设有固定数量的额外存储位置可用,每个存储位置都能够存储 O(log2n) 位的单词”。我不知道他们为什么称之为 O(1) 额外空间。
【解决方案2】:

我对算法的想法:

具有类似于基于分区的一般选择的枢轴点。 http://en.wikipedia.org/wiki/Selection_algorithm

围绕枢轴交换值旋转,直到所有负数都在数组的一个分区中(所有正数都在它之后..或者可能围绕它)

但是,这种交换会稍微影响排序。 我将解释如何更正负数的顺序(您也会这样做以更正正数的顺序)。

每次交换两个数字时.. 更改数字的符号。

这意味着如果您通过负数分区,所有正数都是被交换的负数。这意味着一个正数和下一个正数之间的所有负数都应该在第一个正数之前。把它们全部交换一遍(连续不应该太多,所以你应该得到 O(N))

negs = -4,-5,-6
pos = 1,2,3
ans = -4,-5,-6,1,2,3

1,2,-4,-5,3,-6

i->-4  j->-5
-4 and -5 are both negative.. decrease i by one

1,2,-4,-5,3,-6
i->2 j->-5
swap.

1,5,-4,-2,3,-6
i->1 j->3
1 and 3 are both positive, increase j by one (take turns at changing i,j)

1,5,-4,-2,3,-6
i->1 j->-6
swap.

6,5,-4,-2,3,-1

#now we have negs at start, pos at end of array.
#now do corrections using signs as notification of what was swapped
#we had a counter that told us there were 3 negs.and 3 pos.
#fix first 3 negs.. 6,5,-4 should go to -4,-5,-6
(can tell order by. non swapped negs always come before swapped negs..in the order they are in.. negs are in reverse order)
i'll leave you to implement algorithm for it.

【讨论】:

  • (1,2,-4,-5,3,-6) ---> (-4,2,1,-5,3,-6) ---> (- 4,-5,1,-2,3,-6) ---> (-4,-5,-6,-2,3,-1) ---> (-4,-5,-6 ,2,1,3) 5 步后,1 和 2 的顺序不正确。我有什么问题吗?
  • 你能看到如何将 6,5,-4 变成 -4,-5,-6 吗? -4 应该先去,因为它还没有被交换。 6,5 以相反的顺序出现在 -4 之后(因为交换会翻转它们)。因此,您的算法只需要从左到右遍历列表,将翻转的列表交换到末尾,每次执行时将末尾减 1。并将未翻转的移到前面的位置..
  • 非交换的 negs 总是在交换的 negs 之前出现.. 我不这么认为,例如 1,-4,2,-5,3,-6 -->(i=2,j =-5,swap to[1,-4,5,-2,3,-6]) --> (i=-4,j=3,然后j加一,i减一,i=1 ,j=-6,swap to [6,-4,5,-2,3,-1]),然后修正3个否定[6,-4,5] --->(i=6,j=- 4,切换到[4,-6,5],然后切换到[-4,-6,-5])。所以非交换和交换是混合的,你如何修复正确的结果?顺便说一句,这个算法将是 O(nlogn),而不是 O(n),因为你应该很长时间地遍历所有左数,T(2n)=2T(n)+n=nlogn。
  • 我的算法中让你到达 [6,-4,5] 的部分是 O(N)。现在您需要将负数移到前面,将正数反转并移到后面,得到 [-4,5,6] 的答案。我声称这最后一步也可以在 O(N) 中完成。我在我的代码末尾处理它,但没有时间完成如何去做。
  • 好吧,我不得不说谢谢。您不仅要提出您的想法,还要实施您的想法。这就是我从你那里学到的,要认真。和你讨论得很愉快。
【解决方案3】:

这段代码大部分都是这样。我只是没有完成它反转x,j和j,y之间交换值的部分。 (你可以原地反转..我还没有这样做)。

无论如何.. 恐怕我没有时间完成它,但希望你能:

def brute_force(nums):
    neg = [i for i in nums if i<0]
    pos = [i for i in nums if i>=0]
    return neg+pos

def in_place(nums,i,j,depth):
    x,y = i,j
    print 'running on ',nums[i:j+1]
    if j-i==1:
        a,b = nums[i],nums[j]
        if a>=0 and b<0:
            nums[i],nums[j] = b,a
        return None
    #print i,j
    while i<j:
        a,b = nums[i],nums[j]
        if (a<0 and b>=0):
            i+=1
            j-=1
        elif (a>=0 and b<0):
            nums[i],nums[j]=-b,-a
            i+=1
            j-=1
        elif a<0:
            i+=1
        else:
            j-=1
    print "changed1 to ", nums
    print nums[x:j+1],nums[j+1:y+1]
    start = (i for i in reversed(nums[x:j+1]) if i>=0)
    for i in range(x,j):
        if nums[i]>=0:
            nums[i]=next(start)
    print "changed2 to ", nums
    end = (i for i in reversed(nums[j+1:y+1]) if i<0)
    for i in range(j+1,y+1):
        if nums[i]<0:
            nums[i]=next(end)
    print "changed3 to ", nums
    if depth == 0:
        in_place(nums,0,j,depth+1)
        in_place(nums,j+1,len(nums)-1,depth+1)







nums = [1,2,-4,-5,3,-6]

print brute_force(nums)
in_place(nums,0,len(nums)-1,0)
print nums
print "going z"
#z = [-2,3,-1]
#in_place(z,0,2,0)
#print z

进一步的例子:

_list = [1,-4,2,-5,3,-6]

def in_place(nums,i,j,depth):
    x,y = i,j
    print 'running on ',nums[i:j+1]
    if j-i==1:
        a,b = nums[i],nums[j]
        if a>=0 and b<0:
            nums[i],nums[j] = b,a
        return None
    #print i,j
    while i<j:
        a,b = nums[i],nums[j]
        if (a<0 and b>=0):
            i+=1
            j-=1
        elif (a>=0 and b<0):
            nums[i],nums[j]=-b,-a
            i+=1
            j-=1
        elif a<0:
            i+=1
        else:
            j-=1
    print "changed1 to ", nums

in_place(_list,0,len(_list)-1,0)

>>>
running on  [1, -4, 2, -5, 3, -6]
changed1 to  [6, -4, 5, -2, 3, -1]

【讨论】:

    【解决方案4】:

    这可以通过改变归并排序算法中的归并函数来完成。

    输入:int[] A, int low, int mid, int high

    开始前循环不变: A[low] 到 A[mid] 有 -ve 数字后跟 +ve 数字,并且具有最初存在于 A[low] 到 A[mid] 中的数字。
    上述条件适用于 A[mid+1] 到 A[high]

    合并步骤:

    1. 跳过所有从低到中为 -ve 的元素,将 +ve 数字的起点保存在变量 j 中。
    2. 将 mid 之前 +ve 的剩余元素复制到临时数组中
    3. 从 A[j] 开始复制从 mid+1 到 high 范围内的所有 -ve 元素,同时递增 j
    4. 将存储在临时数组中的元素复制回从 j 继续的 A
    5. A的后半部分+ve元素已经就位,所以不需要做任何事情

      public static void rearrange(int[] a){
          merge_arrange(a, 0, a.length-1);
      }
      
      public static void merge_arrange(int[] a, int low, int high){
          if(low < high){
              int mid = (low+high)/2;
              merge_arrange(a, low, mid);
              merge_arrange(a, mid+1, high);
      
              merge(a, low, mid, high);
          }
      }
      
      public static void merge(int[] a, int low, int mid, int high){
          ArrayList<Integer> temp = new ArrayList<Integer>();
      
          int i;
          for(i=low;i<=mid && a[i]<0;i++);
      
          int j=i;
          while(i<=mid){
              temp.add(a[i++]);
          }
      
          int k;
          for(k=mid+1;k<=high && a[k]<0;k++){
              a[j] = a[k];
              j++;
          }
      
          for(int num:temp){
              a[j] = num;
              j++;
          }
      }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-17
      • 1970-01-01
      • 1970-01-01
      • 2017-10-30
      • 2013-01-13
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      相关资源
      最近更新 更多