给定一个包含正负整数的数组,编写一个算法,时间复杂度为 O(n),空间复杂度为 O(1),将所有负整数移动到正整数之前并保持相对位置不变。
例如:{1,7,-5,9,-12,15} -----> {-5,-12,1,7,9,15}
你有什么想法吗?
我对算法的想法:
有一个类似于基于分区的普通选择中的枢轴点。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] 有负数,然后是正数,并且具有最初存在于 A [low] 到 A [mid] 中的数字。
上述条件适用于 A [mid + 1] 到 A [high]
合并步骤:
A的第二半部分中的正数元素已经就位,因此不需要做任何操作
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++;
}
}