背景
网上大多数快速排序算法介绍都是挂羊头卖狗肉,为何我有这种感慨,那是因为快速排序是一种不稳定的算法,如果将它改成稳定算法的话,是不是有些四不像,说的再好听一些就是快速排序的一些变种,改进都说不上,因为改的失去了快速排序的原汁原味。但是想不到网上不少人对该算法津津乐道,称之为简单快捷,我愿将之称为伪快排
问题分析:
来进入代码看一看这种类快排或者伪快速排序(不含测试方法):
public static void quicksortbyleft(int[]arr , int left,int right) {
//注意递归函数必须有返回值,下面为递归的结束条件,如果缺少就会报错,这里会报数组下标越界.
if(left>=right) {
return;
}
int l = left;
int r = right;
int temp =0;
int pivot = arr[left];
while(l!=r) {
while(arr[r]>=pivot && l<r) {
r--;
}
while(arr[l]<=pivot && l<r) {//这里应该大于等于要交换,这才是快速排序,是不稳定的
l++;
}
if(l!=r) {
temp = arr[r];
arr[r] =arr[l];
arr[l]=temp;
}
}
//把l与r共同指向的值赋值给基准值,即left
arr[left] = arr[l];//这里可以填arr[l]==arr[r]
//把基准值放到最终的位置上
arr[l]=pivot;
//递归,让l+1指向右半部分第一个值,r-1指向左半部分最后一个值
quicksortbyleft(arr, left,r-1);
quicksortbyleft(arr, l+1, right);
}
代码中有详细的解释,如果你有一个序列带入的话,会发现结果很可观,同时各个阶段都很清晰,比如跳出while循环就是l==r的时候,就是这次递归将要结束,给基准赋值等。整套流程下来是不是行云流水,水到渠成的痛快,但是转念一想,不对啊,老哥,你这两个小while找交换值最后出来个值相等也略过是什么鬼,那岂不是说,查到和基准值一样的数值,直接过去了,这有点说不过去了,按照快速排序是不是要交换一下,哎,年轻人不讲武德。
解决方案:
public static void quicksort(int[] arr,int left,int right) {
int l = left;
int r = right;
int mid = left;
int temp = 0;
while(l!=r) {
while(arr[l]<arr[mid]) {
l++;
}
while(arr[r]>arr[mid]) {
r--;
}
//当l>=r,结束这个while循环,实际上此时l==r
if(l>=r) {
break;
}
//交换大小值
temp = arr[l];
arr[l]=arr[r];
arr[r]=temp;
if(arr[l]==arr[mid]) {
//这里可以避免死循环。如{-9,3,0,23,3,3,3,-567,70}中当下//标l=4,r=6,mid=5时的值相同,换来换去都一样的尴尬局面。
r--;
}
//作用同上
if(arr[r]==arr[mid]) {
l++;
}
}
//当l=r时,让r-1,到左半部分;让l+1到右半部分
if(left<r-1) {
quicksort(arr, left, r-1);
}
if(l+1<right) {
quicksort(arr, l+1, right);
}
}
这里借鉴了按照中间值快速派排序的思路和实现方式,另外快速排序还有很多不错的优化与创新,还有不少常用的排序算法比如内省排序、bfptr排序都用到了快速排序,实现在最坏时间复杂度下依旧有良好的时空高性能,有机会还会记录下来。