排序算法之快速排序

排序算法之快速排序

  • 做算法作业的时候遇到了一道题目,题目答案里提示和快速排序的时间复杂度相同,便动手学习了一下快速排序。快速排序的主要思想应该也是分治。就是在原数组a中选择一个值作为枢纽(pivot),然后把所有比它小的值放在它左边,把比它大的值放在右边。然后再用类似的方法递归求解。值得注意的是,当第一个枢纽找到了它的位置后,比如下标为k,那么它在之后的递归过程中是不会改变的,因为它就应该在这个位置。

  • 以下是我写的程序。

    #include <iostream>
    using namespace std;
    
    void quicksort(int a[], int left, int right)
    {
        if (left > right)
            return;
        int i = left, j = right, pivot = a[left]; //i是左边哨兵,j是右边哨兵,pivot储存枢纽
        while (i != j)
        {
            while (a[j] >= pivot && i < j) //先从右边开始找比枢纽小的
                j--;
            while (a[i] <= pivot && i < j) //再从左边开始找比枢纽大的
                i++;
            if (i < j) //交换两个数在数组中的位置
            {
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
        }
        a[left] = a[i]; //最终将基准数归位
        a[i] = pivot;
        quicksort(a, left, i - 1);  //继续处理左边的,i已经是枢纽最终的位置,不会再改变
        quicksort(a, i + 1, right); //继续处理右边的
    }
    
    int main()
    {
        int a[11] = {0, 6, 1, 2, 7, 9, 3, 4, 5, 10, 8}; //一共十个数,a[0]弃置
        quicksort(a, 1, 10);                            //快速排序调用
        for (int i = 1; i <= 10; i++)                   //输出排序后的结果
            cout << a[i] << " ";
        cout << endl;
        return 0;
    }
    
  • 程序运行结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YhvdUkhi-1618287083590)(排序算法之快速排序.assets/3.png)]

  • 值得注意的是,这个程序的在把枢纽小的数放在枢纽左边,把枢纽大的数放在枢纽右边这个过程中的方法是比较独特的。和之前数据结构书本上的疯狂交换不同,它先从右边开始找,找到一个比枢纽小的数,然后停止,定位在哪个下标,之后再从左边开始找,找到一个比枢纽大的数,然后停止,定位在哪个下标。很显然,这两个数的位置都是不对的,因为比枢纽小的应该在枢纽左边,而它在右边,比枢纽大的应该在右边,而它在左边。所以直接把这两个位置不正确的数据进行交换即可。这样一下子解决两个数据。所以在这个过程中,我们是没有去管枢纽的,所以省去了把枢纽换来换去的时间,提升了程序效率。

  • 下面我举一下第一个枢纽“6”的找到它真正位置的过程。

    6 1 2 7 9 3 4 5 10 8	//把第一个位置的值(6)看作枢纽
    i = 1; j = 10;	//左哨兵和右哨兵
    右哨兵先开始寻找第一个比枢纽小的数字,至于为什么是右哨兵开始,等等会解释
    j--; ...
    当j = 8的时候发现5比枢纽小,j停止
    i++; ...
    当 i = 4的时候发现7比枢纽大,i停止
    比较i 和 j是不是相等,发现不相等
    于是把这两个数进行交换
    6 1 2 5 9 3 4 7 10 8
    j--; 
    当 j = 7的时候发现4比枢纽小,j停止
    i++; 
    当 i = 5 的时候发现9比枢纽大, i停止
    i 和 j不相等,进行交换
    6 1 2 5 4 3 9 7 10 8
    j--;
    当j = 6时发现3比枢纽小,j停止
    i++;
    i 变成 6,这时候 由于 i == j 退出循环(程序13行和程序9行)
    之后把枢纽和i位置的数据一换,变成
    3 1 2 5 4 6 9 7 10 8
    我们惊奇的发现枢纽成功找到了它的位置,且它左边的数都比他小,右边的数都比他大。
    
  • 我们再来讲一讲为什么一开始一定要j也就是右哨兵开始动,找到一个比枢纽小的值,然后再左哨兵动,找到一个比枢纽大的值,他们反正最后都是交换,谁先动不是都一样的嘛?

  • 原因就在于当 i == j 的时候跳出循环后,枢纽需要和 i 的位置进行交换。一般来说我们的枢纽都是选择数组的第一个元素,所以这样交换,需要满足一个条件,那就是 第 i 个位置的元素比枢纽的值小。而我们如果先让右哨兵动,让它一直自减,直到遇到一个比枢纽小的数,然后左哨兵才动,一直自增,最后i == j,但是这个位置是右哨兵找到的,只是左哨兵撞上了,所以 i 或者说 j 这个位置的值是小于枢纽的。所以跳出while循环后,枢纽和 i 位置的元素的交换是正确的。

  • 那怕你不够信服,我们就来模拟一下,如果左哨兵先动会发生什么。

    6 1 2 7 9 3 4 5 10 8	//把第一个位置的值(6)看作枢纽
    i = 1; j = 10;	//左哨兵和右哨兵
    i++; ...
    当 i = 4的时候发现7比枢纽大,i停止
    j--; ...
    当j = 8的时候发现5比枢纽小,j停止
    比较i 和 j是不是相等,发现不相等
    于是把这两个数进行交换
    6 1 2 5 9 3 4 7 10 8
    i++; 
    当 i = 5 的时候发现9比枢纽大, i停止
    j--; 
    当 j = 7的时候发现4比枢纽小,j停止
    i 和 j不相等,进行交换		//我们看到到这里所有的结果和右哨兵开始的结果都是一样的,但是马上就会产生区别
    6 1 2 5 4 3 9 7 10 8
    i++; ...
    当i = 7的时候 发现 9大于枢纽, i停止
    因为 j = 7,所以j不能再自减了,直接跳出while循环
    之后把枢纽和i位置的数据一换,变成
    9 1 2 5 4 3 6 7 10 8
    我们看到在最后一步,也就是枢纽和 i 位置的元素交换时,产生了错误
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GXruD5ej-1618287083593)(排序算法之快速排序.assets/4.png)]

  • 所以快速排序算法我们需要注意几个问题:如何把比枢纽小的换到左边,比枢纽大的放到右边 以及 右哨兵先动还是左哨兵先动

  • 参考链接

    排序算法——快速排序(Quicksort)基准值的三种选取和优化方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值