Algorithms-2.3 QuickSort 快速排序

QuickSort

1 快速排序 Quicksort

1.1 基础的快速排序

在这里插入图片描述

  • quicksort也是一种递归(recursive)方法,quicksort的递归是在处理整个数组后,而归并排序是在它处理整个数组前递归
  • 切分(partition)的位置取决于数组的内容
    在这里插入图片描述
  • 通过递归的调用切分来排序
    在这里插入图片描述
  • 一边遇到不合法的值暂停后,另一边不暂停,直到另一边也找到不合法的值,二者互换位置
  • 当j在i左边,i在j右边时停止;此时j指向左边子数组中最右边的元素,i指向右边子数组中最左边的元素
    在这里插入图片描述
package Chapter02;

import edu.princeton.cs.algs4.StdRandom;

public class Quick {

    //快速排序的切分
    private static int partition(Comparable[] a,int lo,int hi){
        int i = lo, j = hi + 1;//后面要先自加或自减,所有这里要比正常大
        while (true){
            //i从左到右
            while (Riqi.less(a[++i],a[lo])){ //while条件为true的时候一直循环,false才跳出循环
                if (i==hi) break;
            }
            //j从右到左
            while (Riqi.less(a[lo],a[--j])){ //执行j=j-1前,先自减,再a[]赋值
                if (j==lo) break;
            }
            //判断i和j是否cross
            if (i>=j) break; //如果break,后面的代码将不再执行   当i或j所在与a[lo]一致时,取等号(两边都在等号处停止)
            Selection.exch(a,i,j);
        }
        //把partition放到中间
        Selection.exch(a,lo,j);
        return j; //现在的j就是切分点
    }

    public static void sort(Comparable[] a){
        StdRandom.shuffle(a);//消除对输入数据的依赖
        sort(a,0,a.length-1); //递归方法,把子数组的长度作为参数
    }

    public static void sort(Comparable[] a,int lo,int hi){
        if(hi <= lo) return; //保证数组low小于high,且数组长度最小为2
        int j = partition(a,lo,hi);
        sort(a,lo,j-1);//左半部分排序
        sort(a,j+1,hi);//右半部分排序
    }

}

在这里插入图片描述

  • 大小为1的子数组不需要继续切分
  • 何时终止循环很tricky,尤其是遇到元素值相同的情况
    在这里插入图片描述

1.2 复杂度和稳定性在这里插入图片描述

在这里插入图片描述

  • 每次只剥离了最小的元素
    在这里插入图片描述
  • N-1个元素要和partition比较,I和j cross,+2次比较,因此partition需要N+1次compare
  • ()内是k放在不同的位置的所有可能性,每种可能性出现的概率是1/N
  • 省略了(N-1)CN-1 = (N-1)N + 2(C0+C1+…+CN-2),将NCN…式减去省略的式子,得到Subtract展示的式子
    在这里插入图片描述
  • 将第一行的N替换成N-1,N-2…不断代入
  • 快速排序的最坏情况是二次排序,即shuffle后已经是排好的状态
    在这里插入图片描述
    在这里插入图片描述

1.3 快速排序的改进

1.3.1 小的子数组使用插入排序

在这里插入图片描述

	private static final int CUTOFF = 10;
    public static void sort(Comparable[] a,int lo,int hi){
        //if(hi <= lo) return; //保证数组low小于high,且数组长度最小为2
        if(hi <= lo + CUTOFF - 1){ //若子数组长度小于等于10,使用插入排序
            Insertion.sort(a,lo,hi);
        }
        int j = partition(a,lo,hi);
        sort(a,lo,j-1);//左半部分排序
        sort(a,j+1,hi);//右半部分排序
    }
1.3.2 三取样切分

在这里插入图片描述

  • 选择partition之前筛选,使得其大小尽量处于整个数组中间,而不是随机地选择shuffle后的首个元素
  • 抽取3个items,选择其中位数,代价是需要计算中位数
	public static void sort(Comparable[] a,int lo,int hi){
        if(hi <= lo) return; //保证数组low小于high,且数组长度最小为2
        
        int m = median3(a,lo,lo+(hi-lo)/2,hi);
        Selection.exch(a,lo,m);

        int j = partition(a,lo,hi);
        sort(a,lo,j-1);//左半部分排序
        sort(a,j+1,hi);//右半部分排序
    }

	// return the index of the median element among a[i], a[j], and a[k]
    private static int median3(Comparable[] a, int i, int j, int k) {
        return (Riqi.less(a[i], a[j]) ?
               (Riqi.less(a[j], a[k]) ? j : Riqi.less(a[i], a[k]) ? k : i) :  //x ? y : z
               (Riqi.less(a[k], a[j]) ? j : Riqi.less(a[k], a[i]) ? k : i)); //如果x==true, 则结果为y,否则结果为z
    }
  • 使用了三取样切分和插入排序转换的快速排序
    在这里插入图片描述
1.3.3 熵最优的排序
  • 熵最优(entropy-optimal):不管相同键的分布是什么,它比较的次数与能做到的最好程度成正比
  • 见 3.1 三向切分的快速排序

2 选择 Selection

  • 目标是寻找数组中第k小的元素
    在这里插入图片描述
	//应用:quick-select
    public static Comparable select(Comparable[] a, int k){
        StdRandom.shuffle(a);
        int lo = 0, hi = a.length - 1;
        while (hi > lo){
            int j = partition(a,lo,hi);
            if       (j < k)    lo = j+1;
            else if  (j > k)    hi = j-1;
            else return         a[k];
        }
        return a[k];
    }

在这里插入图片描述

3 重复键 Duplicate keys

在这里插入图片描述
在这里插入图片描述

3.1 三向切分的快速排序

在这里插入图片描述
在这里插入图片描述

  • 注意lt,i和gt的起始位置
  • lt指向分区元素,其实i每次都与lt比,lt是最开始的lo,一直不变
  • i与gt交换后i不增加,因为新的i(值为Z)还没与v
  • 当i与gt重合是最后一次compare,gt右边的都比v大
    在这里插入图片描述
    在这里插入图片描述
package Chapter02;

public class Quick3way {
    private static void sort(Comparable[] a,int lo,int hi){
        if (hi <= lo) return;
        int lt = lo, gt = hi;
        Comparable v = a[lo];
        int i = lo + 1;
        while (i <= gt){
            int cmp = a[i].compareTo(v); //a[i] - v
            if       (cmp < 0)   Selection.exch(a, lt++, i++); //a[i] < v   i++先赋值再自增:i和lt先换完位置,再分别+1
            else if  (cmp > 0)   Selection.exch(a, i, gt--);   //a[i] < v
            else                 i++;                          //a[i] == v
        } //现在a[lo...lt-1] < v = a[lt...gt] < a[gt+1...hu]成立
        sort(a,lo,lt-1);
        sort(a,gt+1,hi);
    }
}

在这里插入图片描述
在这里插入图片描述

4 系统排序 System sorts

在这里插入图片描述

  • 对于object使用mergesort是因为此时程序员不在意是否占用额外的内存空间
  • 如果程序使用基本类型,也许性能是最重要的,因此用快速排序
    在这里插入图片描述
    在这里插入图片描述
  • 有的系统设计师不喜欢shuffle,因此这种方法改变了系统的状态,会采取tukey的方式找到median
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值