快速排序

 参考: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上遇到快排序时,我记得这个方法是选定一个基准,将比它小的放在它的左侧,比它大的放在它的右侧。然后在左侧区间与右侧区间重复这个过程,即递归。

然而实现时,我只是将中间元素做基准,两根指针分别从头和尾开始遍历,头指针循环找到比基准大的,尾指针循环找到比基准小的,然后交换。重复这个过程直到头指针等于尾指针。

然后递归左右区间。

运行的时候结果不对。

仔细思考了下,这个中间元素固定的话很有问题,你无法保证它的值一定是数组中间值。所以选定的基准也应该参与移动过程,找到它应该在的位置。

 

原文地址:https://www.cnblogs.com/Tang-tangt/p/8847819.html