排序算法之——三路快排分析

引言

快速排序分析中我们探讨了经典快排的实现,且进行了一些小优化。

但是若序列中包含大量重复的元素,这种情况下,快排的性能就不那么理想了。

下面开始学习三路快排的思想吧。

思路

将数组分为三部分,分别对应于小于、等于和大于哨兵元素v的子序列。

在这里插入图片描述

切分方法为从左到右遍历(扫描)数组一次,维护一个指针lt使得a[left..lt-1]中的元素都小于v,一个指针gt使得
a[gt+1..right]中的元素都大于v,一个指针i使得a[lt..i-1]中的元素都等于v,而a[i..gt]中的元素是还未扫描的。

一开始令i = left + 1,对a[i]v进行比较,根据比较情况作出不同的处理:

  • a[i]小于v,将a[lt]a[i]交换,将lti加1;
  • a[i]大于v,将a[gt]a[i]交换,将gt减1;
  • a[i]等于v,将i加1

以上操作会不断缩小gt - i的值,直到i > gt扫描结束。这时就成了上图切分后的情况。

在这里插入图片描述
假定元素数据如上图。

在这里插入图片描述
先让这三个指针就位。我们选取的哨兵元素v为6。

在这里插入图片描述
a[i]=3小于6,交换a[lt]a[i]:6和3,lti指针后移。

在这里插入图片描述

a[i]=8大于6,交换a[gt]a[i]:7和8,gt指针前移。

在这里插入图片描述
a[i]=7大于6,交换a[gt]a[i]:1和7,gt指针前移。

在这里插入图片描述
a[i]=1小于6,交换a[lt]a[i]:6和1,lti指针后移。

在这里插入图片描述
a[i]=2小于6,交换a[lt]a[i]:6和2,lti指针后移。

在这里插入图片描述
a[i]=1小于6,交换a[lt]a[i]:6和1,lti指针后移。

在这里插入图片描述
a[i]=6等于6,i指针后移。直到a[i]=5,交换6和5,lti指针后移。

在这里插入图片描述
i继续后移,然后不满足i <= gt的条件啦,跳出循环~

数组就被分为三部分,分别对应于小于、等于和大约哨兵元素v的子序列。

一次切分就完毕了,接下来只要对小于和大于v的部分进行同样的切分即可。

代码

private static <E extends Comparable<? super E>> void quick3Way(E[] a) {
    shuffle(a);
    quick3Way(a, 0, a.length - 1);
}


/**
 * @param a
 * @param left
 * @param right
 * @param <E>
 */
private static <E extends Comparable<? super E>> void quick3Way(E[] a, int left, int right) {
    if (right <= left) {
        return;
    }
    int lt = left, i = left + 1, gt = right;
    E v = a[left];
    //当 i > gt时跳出循环
    while (i <= gt) {
        int cmp = a[i].compareTo(v);//这里有个坑,注意和普通快排算法不一样,a[left]的元素是会发生变化的,因此需要用临时变量v缓存起来

        if (cmp < 0) {
            //a[i]小于v,将a[lt]和a[i]交换,将lt和i加1;
            swap(a, lt++, i++);
        } else if (cmp > 0) {
            //a[i]大于v,将a[gt]和a[i]交换,将gt减1;
            swap(a, gt--, i);
        } else {
            //a[i]等于v,将i加1
            i++;
        }
    }
    quick3Way(a, left, lt - 1);
    quick3Way(a, gt + 1, right);
}
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愤怒的可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值