参考:https://blog.csdn.net/morewindows/article/details/6684558
正如文中所说,最好能用自己的话总结算法过程(思路),这样印象深刻,有助于理解。
快排序也称为分治法,更恰当地说是挖坑填数+分治法。
挖坑填数:
设置头尾两个指针,初始i=0,j=size-1。
选定一个基准数,可以是数组第一个值(即i处数值),将其赋值给临时变量x。由于将该值保存了下来,就相当于在原数组对应位置处挖了一个坑,可以将其他数值填进来。
从尾部开始寻找小于x的数值,将其赋值给A【i】。即用A【j】填上一步 i 处的坑,同时,j 处形成了新的坑。
从头部开始寻找大于等于x的数值,将其赋值给A【j】。即用A【i】填上一步 j 处的坑,同时,i 处又形成了新的坑。
重复上述挖坑填坑过程直到两根指针相等 i==j,此时i(j)左侧都是小于x的数,右侧都是大于等于x的数,并且 i 处形成新的坑,将 x 值填进来。
然后分治,对 左侧第一个数 ~ i-1 与 i+1 ~ 右侧最后一个数 这两个区间重复上述过程,直到传入的头尾指针相等。
核心代码:
void qSort(vector<int> &A,int l,int r)
{
if (l>=r)
{
return ;
}
int x=A[l];//取A[l]为基准,也就是第一个坑;
int i=l,j=r;
while(i<j)
{
//从右向左找小于x的数,填入坑中;
while(i<j&&A[j]>=x)
{
j--;
}
if (i<j)
{
A[i]=A[j];//A[j]填入坑中,j处形成新的坑;
i++;
}
//从左向右找大于等于x的数,填入坑中;
while(i<j&&A[i]<x)
{
i++;
}
if (i<j)
{
A[j]=A[i];//A[i]填入坑中,i处形成新的坑;
j--;
}
}
//循环结束时,i==j,将x填入坑中;
A[i]=x;
qSort(A,l,i-1);
qSort(A,i+1,r);
}
PS:关于元素相等情况的分析。
假设是大于等于(小于等于前移情况同理)基准X的数向后移,某个等于基准的数,被填到了基准确定位置id的后面,比如end。
则下一次分治时,在右侧区间,即在【id+1,end】这个区间完成挖坑填数时,以id+1对应的数为基准。
注意,该数一定是大于等于上一个X也就是当前end对应的值。若大于,end处值小于当前基准,被填坑到了前面,是正确的升序。若等于,相当于重复前面的分析,end处值一定会被填到比它大的数的前面,除非右侧区间全部是相同的数。如果右侧区间全部是相同的数,顺序天然是正确的升序,end处数不需要再移动结果也是正确的。
所以快排序适用有重复元素的情况。
快排序其他思路讲解:https://www.cnblogs.com/grandyang/p/5637862.html
——————————————————分割线————————————————
在lintcode上遇到快排序时,我记得这个方法是选定一个基准,将比它小的放在它的左侧,比它大的放在它的右侧。然后在左侧区间与右侧区间重复这个过程,即递归。
然而实现时,我只是将中间元素做基准,两根指针分别从头和尾开始遍历,头指针循环找到比基准大的,尾指针循环找到比基准小的,然后交换。重复这个过程直到头指针等于尾指针。
然后递归左右区间。
运行的时候结果不对。
仔细思考了下,这个中间元素固定的话很有问题,你无法保证它的值一定是数组中间值。所以选定的基准也应该参与移动过程,找到它应该在的位置。