普林斯顿公开课 算法3-3:三路快排

很多时候排序是为了对数据进行归类,比如对城市进行排序,对员工的职业进行排序。这种排序的特点就是重复的值特别多。

如果使用普通的快排对这些数据进行排序,会造成N^2复杂度,但是归并排序和三路快排就没有这样的问题。

三路快排

三路快排的基本思想就是,在对数据进行分区的时候分成左中右三个部分,中间都是相同的值,左侧小于中间,右侧大于中间。

性能

三路快排的复杂度比普通快排小,主要取决于数据中重复数据的数量。重复数据越多,三路快排的复杂度就越接近于N。

代码

public class Quick3 {
    public static void sort(Comparable[] a) {
        Shuffle.shuffle(a);
        sort(a, 0, a.length);
    }

    public static void sort(Comparable[] a, int lo, int hi) {
        // 如果数组长度为0,直接返回
        if(lo >= hi) {
            return;
        }

        // 将第一个元素作为分区依据
        Comparable mid = a[lo];

        // 对数组进行扫描,分区
        int lt = lo;
        int gt = hi-1;
        int i = lo;
        while(true) {
            // 指针发生了交叉,退出循环
            if(i > gt) { // 注意,这里不是i >= gt,i=gt的时候仍然需要排序
                break;
            }

            int cmp = a[i].compareTo(mid); // 注意,这里不要用SortUtil.less

            if(cmp < 0) {
                SortUtil.exch(a,lt,i);
                lt++;
                i++;
            } else if(cmp > 0) {
                SortUtil.exch(a,gt,i);
                gt--;
            } else {
                // 跳过等值的情况。这就是为什么三路快排适合排序等值多的数组
                i++;
            }
        }

        // 迭代排序
        sort(a,lo,lt);
        sort(a,gt+1,hi);
    }
}

普林斯顿公开课 算法3-3:三路快排

时间: 2024-10-15 20:08:48

普林斯顿公开课 算法3-3:三路快排的相关文章

普林斯顿公开课 算法3-4:快排的应用

排序的应用 排序算法有着广泛的应用. 典型的应用有 对名称进行排序 排序MP3音乐文件 显示Google的搜索结果 按标题顺序列出RSS订阅 排序之后下列问题就变得非常简单了 找出中位数 数据库中的二分查找 找出统计数据中的异常值 在邮箱中找出重复的邮件 不是特别典型的应用有 数据压缩 计算机图形 计算生物 负载平衡 编程语言中的排序算法 java中对于基本类型使用快排,对于引用类型使用归并排序.因为归并排序很稳定,而且保证复杂度为NlgN Java.C/C++中的快排都有以下特性: 对于小的子

普林斯顿公开课 算法4-3:堆排

堆排的灵感源自于堆的数据结构.它是一种原地排序算法,不需要额外的临时数组. 基本思想 堆排的基本思想是: 先建立一个最大堆 将最大的元素移动到数组末尾,减小堆的大小,调整最大堆使其符合最大堆的性质 重复第二步,直到最大堆耗尽为止 第一个步骤建立最大堆的代码非常简单,只要对每个节点执行sink操作即可. 1 2 for(int k = N/2; k >= 1; k--)     sink(a, k, N); 第二个步骤也很简单,代码如下: 1 2 3 4 while(N > 1) {     e

普林斯顿公开课 算法1-10:并查集-优化的快速合并方法

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法1-11:并查集的应用

应用 渗透问题 游戏中会用到. 动态连接 最近共同祖先 等价有限状态机 物理学Hoshen-Kopelman算法:就是对网格中的像素进行分块 Hinley-Milner多态类型推断 Kruskai最小生成树 Fortran等价语句编译 形态学开闭属性 Matlab中关于图像处理的bwlabel函数 渗透问题 一个N×N的矩阵,判断顶部和底部是否连通就是渗透问题. 下图中左侧的矩阵能渗透,右侧矩阵不能渗透. 渗透问题在电学.流体力学.社会交际中都有应用. 在游戏中可能需要生成一张地图,但是作为地图

普林斯顿公开课 算法2-1:排序概述

目标 对所有类型的数据进行排序. 问题 排序函数如何知道比较的是哪种类型的数据呢? 回调函数 这时候就需要引入回调函数的概念了.回调函数就是将可执行的代码作为参数进行传递. 实现回调的方法 在Java中可以通过接口来实现,在C语言中可以通过函数指针来实现,C++中可以通过class-type functor,也就是重载操作符operator ()的类,在C#中可以使用Delegate委托,在Python/Perl/ML/javascript中可以直接传递函数. JDK中提供了Comparable

普林斯顿公开课 算法1-1:算法分析

为什么要分析算法 分析算法能够预測算法的性能,比較算法之间的优劣,保证算法的正确性,理解算法的理论基础. 成功算法的样例 离散傅立叶变换,假设使用暴力方法,那么算法的复杂度是是N^2,假设使用FFT高速傅立叶变换能够实现O(N logN)复杂度 N-body模拟:使用Barnes-hut算法能够将复杂度减少到N logN 顺便发一张N-body模拟的炫图 Barnes-Hut算法示意图 算法分析的步骤 观察问题的特征和想到得到的结果 依据观察结果提出如果 使用如果来预測可能发生的情况 检測预測结

普林斯顿公开课 算法1-2:观察

这章通过一个简单的例子,详细说明算法分析的步骤. 算法 问题 给定N个不同的整数,从中任意取出三个整数.请问有几种情况,使得取出的3个整数之和为0? 解法 可以使用暴力算法,代码如下: 1 2 3 4 5 6 7 8 9 for(int i=0;i<N;i++){     for(int j=0;j<N;j++){         for(int k=0;k<N;k++){             if(a[i]+b[i]+a[k]==0){                 count+

普林斯顿公开课 算法1-3:数学模型

本节主要通过建立数学模型,来计算算法的运行时间. 公式 算法的运行时间=所有操作的开销乘以操作的次数之和 开销 下表展示了各种操作所需要的时间(单位:纳秒) 整数加法 2.1 整数乘法 2.4 整数除法 5.4 浮点加法 4.6 浮点乘法 4.2 浮点除法 13.5 sin 91.3 arctan 129.0 举例 问题 计算数据中0的个数 代码 1 2 3 4 int count = 0; for (int i= 0; i < N; i++)     if (a[i] == 0)       

普林斯顿公开课 算法1-5:算法理论

本节主要讲解的是算法的复杂度. 算法性能 算法的性能分为三种: 最佳情况:计算时间最短的情况 最差情况:计算时间最长的情况 平均情况:随机输入的期望开销 以二分查找为例 最佳情况是1,因为第一次就有可能找到需要找的整数. 最差情况是logN 平均情况是logN 算法复杂度 算法复杂度用于定义问题的难度,另外也有助于开发最优化的算法,算法复杂度可以通过分析最坏情况来减少输入数据对算法性能的影响. 为了简化问题难度的表示方法,算法复杂度减少了算法分析的细节,忽略常数系数. 最优算法 所谓的最佳算法就