O(nlogn)快速排序-双路排序+详细注解

排序使用的相关工具类与接口
在这里插入图片描述
运行效率

    /**
     * 这时一个双路快速排序
     * 这是一个双路快速排序
     * 数组长度[2000] 值范围[1-2000] 消耗的时间为[1]毫秒
     * 数组长度[4000] 值范围[1-4000] 消耗的时间为[1]毫秒
     * 数组长度[8000] 值范围[1-8000] 消耗的时间为[1]毫秒
     * 数组长度[16000] 值范围[1-16000] 消耗的时间为[1]毫秒
     * 数组长度[32000] 值范围[1-32000] 消耗的时间为[4]毫秒
     * 数组长度[64000] 值范围[1-64000] 消耗的时间为[4]毫秒
     * 数组长度[128000] 值范围[1-128000] 消耗的时间为[7]毫秒
     * 数组长度[256000] 值范围[1-256000] 消耗的时间为[16]毫秒
     * 数组长度[512000] 值范围[1-512000] 消耗的时间为[34]毫秒
     * 数组长度[1024000] 值范围[1-1024000] 消耗的时间为[72]毫秒
     * 数组长度[2048000] 值范围[1-2048000] 消耗的时间为[147]毫秒
     * 数组长度[4096000] 值范围[1-4096000] 消耗的时间为[306]毫秒
     * 数组长度[8192000] 值范围[1-8192000] 消耗的时间为[644]毫秒
     * 数组长度[16384000] 值范围[1-16384000] 消耗的时间为[1335]毫秒
     * 数组长度[32768000] 值范围[1-32768000] 消耗的时间为[2754]毫秒
     * 数组长度[65536000] 值范围[1-65536000] 消耗的时间为[5657]毫秒
     * 数组长度[131072000] 值范围[1-131072000] 消耗的时间为[11612]毫秒
     * 数组长度[262144000] 值范围[1-262144000] 消耗的时间为[24653]毫秒
     */
    @Test
    public void quickSort2WaysMethod() {
        ArraysSort arraysSort = new QuickSort2WaysMethod();
        SortHelper.arraySort(arraysSort, 2000, 20);
    }

代码实现


    private final static Random RANDOM=new Random();

    @Override
    public String getSortName() {
        return "这是一个双路快速排序";
    }

    @Override
    public int[] arraySortMethod(int[] ints) {
        optimizeSort(ints, 0, ints.length - 1);
        return ints;
    }

    /**
     * 双路快速排序
     * 优化后快速排序分配方法
     *
     * @param ints
     * @param l
     * @param r
     */
    public void optimizeSort(int[] ints, int l, int r) {
        //在出现左索引等于右索引的时候
        //它们都是指向同一索引所以没有相比的必要
        //这里直接返回
//        if (l >= r) {
//            return;
//        }

        //在数据量较小的时候使用插入排序
        //这是因为经过了r-l>15的快速排序后 小一边和大一边的模糊排序后
        //在r-l<=15 的时候之间的最大差已经很小了
        //在最大差越小的情况下顺序可能是越有序的
        //说不定已经近乎有序的了 优化的快速排序对近乎有序的排序效率非常高
        //优化后的插入排序对近乎有序的数组进行效率相对较高
        //插入排序相对归并排序减少了数组交换的过程
        if (r - l <= 15) {
            InsertSortMethod.insertSortMethod(ints, l, r);
            return;
        }

        //个人理解快排核心
        //每次遍历排序对l-r之间的数组进行区分
        //好处1.每次遍历可以定位一个p索引在ints数组中确定的位置
        //好处2.因为每次排序都对l-r之间的数据进行大小划分做了一次模糊的排序
        //保证下次排序l-r排序之间最大差越来越小
        //所以下次遍历都比上次排序更加有序效率相对更快
        int p = optimizeQuickSort(ints, l, r);
        //以p中心点-1和+1为界限继续递归快速排序
        optimizeSort(ints, l, p - 1);
        optimizeSort(ints, p + 1, r);
    }

    /**
     * 双路快速排序
     * partition
     * 优化后的排序算法逻辑
     * 之前的排序算法逻辑是从左到右排序算法逻辑有个问题
     * 在处理重复数据较多的情况下 可能会出现等于当时比较的元素较多的情况
     * 等于的那个元素要么在<=v方或者>=方 这样就会造成其中一段分配的元素数量极度不平衡
     * 为避免这种情况 让相等于当时比较元素的元素 相对平均的分配到每一边
     * 所以改为从两边向中间递进 遇到相等时的元素时交换一下
     * 这样在处理元素差较小又相对有序的数组的时候可以更加平均的把相等的元素分配到两边
     *
     * @param ints 整个数组
     * @param l    当前最小索引
     * @param r    当前最大索引
     */
    private int optimizeQuickSort(int[] ints, int l, int r) {
        //随机获取交换一个需要对比的索引
        //如果不懂 可以看下面注释
        SortHelper.swap(ints, l, RANDOM.nextInt(r - l + 1) + l);
        //以交换后第l个索引为为比较对象
        int v = ints[l];
        //初始化左开始索引 和 右开始索引
        int i = l + 1, j = r;
        //开始循环啦
        while (true) {
            //从左循环开始
            //始终保持i<=r 不会超出循环界限 如果inst[i]<v 则继续循环
            //如果等于或者大于v则跳出当前循环 找到下一个需要交换的索引指针
            while (i <= r && ints[i] < v) {
                i++;
            }
            //从右循环开始
            //始终保持j>=i 不会超出循环界限 如果inst[i]>v 则继续循环
            //如果等于或者小于v则跳出当前循环 开始准备i与j交换索引
            while (j >= i && ints[j] > v) {
                j--;
            }
            //如果i>j 说明其中一段已经遍历到另一端的域中
            //说明已经遍历完成了 直接退出循环
            if (i > j) {
                break;
            }
            //通过以上判断后 确认有出现相等或者小一端有大于v的数据或者大一端有小于v的数据
            //开始交换索引数据
            SortHelper.swap(ints, i++, j--);
        }
        //以上循环完成 j处于大一端的初始位置  i的话就是小一端的结束位置
        //其实这里与i 或者j 交换都可以
        //交换j与l的位置
        SortHelper.swap(ints, l, j);
        return j;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值