【发布时间】: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
给定一个包含正整数和负整数的数组,实现一个算法,花费 O(n) 时间和 O(1) 空间将所有负整数放在所有正整数前面,并保持相对位置。 例如:{1,7,-5,9,-12,15} -----> {-5,-12,1,7,9,15}
你有什么想法吗?
【问题讨论】:
标签: algorithm position sorting
您要求一个稳定的就地分区函数。
论文Stable Minimum Space Partitioning in Linear Time (1992)声称有这样的算法,但一些other SO questions对其可行性提出了质疑。
【讨论】:
我对算法的想法:
具有类似于基于分区的一般选择的枢轴点。 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.
【讨论】:
这段代码大部分都是这样。我只是没有完成它反转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]
【讨论】:
这可以通过改变归并排序算法中的归并函数来完成。
输入:int[] A, int low, int mid, int high
开始前循环不变:
A[low] 到 A[mid] 有 -ve 数字后跟 +ve 数字,并且具有最初存在于 A[low] 到 A[mid] 中的数字。
上述条件适用于 A[mid+1] 到 A[high]
合并步骤:
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++;
}
}
【讨论】: