快速排序也使用了分治策略,它每次将待排序列分成两部分,前一部分的元素值都不比后一部分的元素值大。也就是前一部分都不比某个数大,后一部分都不比这个数小。这个基准的选择会直接影响快速排序的效率。
划分
固定第一个数为基准,以{5,4,3,5,6,12,11,-3,4,5,7}为例,用临时变量保存5,相当于挖了个坑,然后从后往前找到第一个小于基准的数,填入当前坑,原位置形成新坑,然后从前往后找到第一个大于基准的数继续填坑挖坑,最后将基准填入最后的坑。
基准为5,蓝色为左指针,红色为右指针 | 操作 |
---|---|
5,4,3,5,6,12,11,-3,4,5,7 | 临时变量保存5,挖第一个坑 |
—,4,3,5,6,12,11,-3,4,5,7 | 从后往前找第一个小于5的 |
4,4,3,5,6,12,11,-3,—,5,7 | 填坑挖坑,从前往后找第一个大于5的 |
4,4,3,5,—,12,11,-3,6,5,7 | 填坑挖坑,从后往前找第一个小于5的 |
4,4,3,5,-3,12,11,—,6,5,7 | 填坑挖坑,从前往后找第一个大于5的 |
4,4,3,5,-3,—,11,12,6,5,7 | 停止挖坑 |
4,4,3,5,-3,5,11,12,6,5,7 | 将5填入最后一个坑 |
可以看出不能保证与基准相等的元素会集中到某一边,也不能集中到基准附近
private static int partition(int[] nums,int start,int end){
int t=nums[start];
int i=start,j=end;
while(i<j){
while(j>i&&nums[j]>=t){
j--;
}
nums[i]=nums[j];
while(i<j&&nums[i]<=t){
i++;
}
nums[j]=nums[i];
}
nums[i]=t;
return i;
}
如果基准位置为参数指定,代码为:
private static int partition(int[] nums,int start,int end,int pivot){
int t=nums[pivot]; //基准
int pit=pivot; //坑的位置
int i=start,j=end;
while(i<j){
while(j>i&&nums[j]>=t){
j--;
}
nums[pit]=nums[j];
pit=j;
while(i<j&&nums[i]<=t){
i++;
}
nums[pit]=nums[i];
pit=i;
}
nums[pit]=t;
return pit;
}
尝试很久写出不同思路的代码,但是好像没什么必要
private static int partition(int[] nums,int start,int end){
if(start>=end)return start;
int t=nums[start];
int i=start+1,j=end;
while(i<j){
while(nums[j]>=t&&j>i){ //从后往前找到第一个比t小的
j--;
}
while(i<j&&nums[i]<t){ //从前往后找到第一个不比t小的,注意是不比t小
i++;
}
if(i<j){
Swap.swap(nums, i, j);
}
}
//i有可能因为j的原因没有移动也没有比较
if(nums[i]<t){
Swap.swap(nums, start, i);
return i;
}else{
return i-1;
}
}