算法与数据结构(7)—— 快速排序(二路、三路)

前言:

介绍了快排分界的标志点,选取的随机性会大大降低快排退化成O(n^2)算法的概率。

问题:当包含大量相同的元素的数组时

快排仍会退化成O(n^2)级别的算法

我们之前判断的时候,并没有判断等于的情况~   这样会造成一个结果,把相同的放入左右任何一部分,会造成极其的不平衡,仍会退化成O(n^2)算法~


二路快速排序(Quick Sort 2 Ways)

算法思路:

然后从左到右逐渐遍历整个数组。现在将这两部分放到数组的两端,下标i、j分别进行扫码:

  • 从下标 i这个位置向后扫描, 
    • 当扫描的元素e小于v :则继续向后扫描。
    • 当扫描的元素e大于v:
  • 从下标 j这个位置向前扫描, 
    • 当扫描的元素e大于v :则继续向前扫描。
    • 当扫描的元素e小于v:

以上两个下标进行扫码时,有一种情况没有写,其实就是当下标 i扫描的元素大于v,下标 j扫描的元素小于v时,将两个下标所指的元素值交换即可!

最后,当下标i 等于下标j 时,扫描结束。将l 和 j下标所代表的元素交换位置即可。


private static int partition(Comparable[] arr, int l, int r){

        SortTestHelper.swap(arr, l, (int)(Math.random() *(r-l+1)) + l);
        Comparable e = arr[l];

        int i = l + 1, j = r;
        while(true){
            while( i <= r && arr[i].compareTo(e) < 0) i ++;
            while( j >= l && arr[j].compareTo(e) > 0) j --;

            if(i > j) break;

            SortTestHelper.swap(arr, i, j);
            i ++;
            j --;
        }
        SortTestHelper.swap(arr, l, j);

        return j;
    }

注意: 在判定条件中,边界情况只能是 < 或 >,而不是 <= 或 >=

  • 对于arr[i]< v和arr[j]>v的方式,第一次partition得到的分点是数组中间;
  • 对于arr[i]<=v和arr[j]>=v的方式,第一次partition得到的分点是数组的倒数第二个。

因为我们的目的就是要让重复的均匀在两边的数组中,而第二种方式还是会将连续出现相等的值归为一方,这样还是会导致两颗子树的不平衡,还是会出现导致O(n^2)的情况出现。


三路快速排序(Quick Sort 3 Ways)

二路排序只是提升了一下效率,当有大量重复值排序的时候,还是会沦为O(n^2)的排序算法,之前的二路,将数组分成两部分,小于v,大于v,两部分是都含有等于v的,只是说尽可能的均匀分布,当存在大量的重复可能效率还是不好,而三路快排则是多加了一部分等于v~

下面要处理i下标代表的元素e,分以下3种情况:

  • e 等于v :直接纳入绿色部分,即无需处理,下标i后移。
  • e 小于v :在学习之前二路快速排序法,应该有思路了,将下标i的元素值和下标 lt+1(即等于v绿色部分的第一个元素)交换,然后i下标后移,继而判断下一个元素;lt下标后移,代表小于v的元素新增了一个。
  • e 大于v :同理,将下标i的元素值和下标 gt-1(紫色部分的前一个元素)交换,gt下标前移,代表大于v的元素新增了一个。注意此时下标i 无需后移,因为不同于小于v 部分,此时交换后的元素是未处理过的,所以直接判断即可!

最后,当下标i 等于下标gt时,扫描结束。将l 和 lt下标所代表的元素交换位置即可。


private static void sort(Comparable[] arr, int l, int r){

        if( r - l >= 15){
            InsertionSortoptimize.sort(arr, r, l);
            return;
        }

        SortTestHelper.swap(arr, l, (int)(Math.random()*(r-l+1)) + l);
        Comparable v = arr[l];

        int lt = l;
        int gt = r+1;
        int i = l + 1;
        while(i < gt){
            if(arr[i].compareTo(v) < 0){
                SortTestHelper.swap(arr, i, lt+1);
                lt ++;
                i ++;
            } else if(arr[i].compareTo(v) > 0){
                SortTestHelper.swap(arr, gt-1, i);
                gt --;
            }else {
                i ++;
            }
        }
        SortTestHelper.swap(arr, l, lt);

        sort(arr, l, lt-1);
        sort(arr, gt, r);

    }

归并排序,二路,三路快速排序比较

结论

  • 测试一:面临大量无序元素时,快速排序最佳。
  • 测试二:面临近乎有序数组时,归并排序最佳。
  • 测试三:面临包含大量重复元素数组的情况下,三路快速排序比归并排序、快速排序有质变的超越!

总体而言,快速排序的性能是要优于归并排序!一般系统级别的快速排序都会选择三路快速排序,因为它在处理包含大量重复元素时,性能极高,即使不是,它的性能也得到保证,不会太差。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值