题外话:STL中的sort()源码就有下面类似的快排知识——https://feihu.me/blog/2014/sgi-std-sort/

    1. 快排思想:

    一种基于分治的排序算法,将数组分成两个子数组,并对子数组独立排序,当两个子数组有序后整个数组自然有序,与归并排序类似但有区别
    image.png

    1. 快排算法:

      先切分处pos枢纽,再左右两边递归快排。
      写法一:

      1. int partition(vector<int>& a, int l, int r) {//划分数组
      2. int priovt = a[l];
      3. int i = l, j = r + 1;//注意:l...r划分,但每次比较的是a[++i],a[--j]
      4. //使得a[i]<=priovt<=a[j]
      5. while (1) {
      6. while (a[++i] <= priovt)
      7. if (i == r) break;
      8. while (a[--j] >= priovt)
      9. if (j == l)break;
      10. if(i >= j)break;
      11. swap(a[i], a[j]);
      12. }
      13. swap(a[l], a[j]);
      14. return j;//因为最后跳出循环的是i>=j;而此时a[j]<=priovt<=a[i]的,
      15. //并且作为priovt的a[l]没动,所以最后交换a[l],a[j]
      16. }
      17. void quicksort(vector<int>& a, int l, int r) {
      18. if (l >= r) return;//终止循环
      19. int pos = partition(a, l, r);
      20. quicksort(a, l, pos - 1);
      21. quicksort(a, pos + 1, r);
      22. }
      23. void sort(vector<int>& a) {
      24. int n = a.size();
      25. quicksort(a, 0, n - 1);
      26. }

      写法二:(带哨兵)

      1. void quicksort1(vector<int>& b,int l,int r) {
      2. if (l >= r) return;//种植递归
      3. int i = l, j = r;
      4. int temp = b[l];
      5. while (i != j) {
      6. //顺序很重要,因为基准是最左边,先要从右往左比较
      7. while (i != j && b[j] > temp)j--;
      8. while(i != j && b[i] <= temp)i++;
      9. //跳出上面两个循环的条件是 查到了不符合大小的元素
      10. if (i != j) swap(b[i], b[j]);
      11. }
      12. //i==j 获得基准i
      13. b[l] = b[i];
      14. b[i] = temp;
      15. //递归快排
      16. quicksort(b, l, i - 1);
      17. quicksort1(b, i + 1, r);
      18. }
      19. void sort1(vector<int>& b) {
      20. int n = b.size();
      21. quicksort(b, 0, n - 1);
      22. }
    2. 注意事项

    • 别越界:如果切分元素是数组中最小或最大的元素,别让扫描指针跑出数组边界。另外注意考虑到还存在与枢纽相同的元素而导制切分失败,
    • 保持随机性:即随机切分一个元素(指定一个枢纽)
    • 考虑到有重复的元素时,以上快排算法可能会有不必要的等值元素交换,所以推荐“三向切分的快排”
    • 递归中止:当切分元素不能放入正确的位置时,导致切分元素正好是子数组的最大最小值陷入了无限递归。
    1. 算法改进

    1.首先,明确快排适合于不是基本有序的,大数组情况,而对于小数组,快排比插入排序慢,这时可以切换到插入排序,具体的M一般选取5~15.
    修改如下:

    1. void quicksort(vector<int>& a, int l, int r) {
    2. if (l >= r) return;//终止循环
    3. /*************/
    4. if (l + m >= h) {insertsort(a,l,h);return ;}
    5. /************/
    6. int pos = partition(a, l, r);
    7. quicksort(a, l, pos - 1);
    8. quicksort(a, pos + 1, r);
    9. }
    1. 2.**三向切分快排:适用于重复元素较多的情况,能将OOnlogn)降到On)级别**<br />因为有较多重复元素,所以相比一般快排,采取三个指针来维护数组,lt使a[lo...lt-1]都小于privotgt使得a[gt+1..h]都大于privot,指针i使a[lt...i-1]都等于privota[i...gt]还未确定。<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21761198/1624968970257-e4fd0dc7-9a64-40d1-bca6-846e0459dacd.png#clientId=u67b511d2-204d-4&from=paste&height=192&id=u59d7ed7c&name=image.png&originHeight=256&originWidth=516&originalType=binary&ratio=2&size=25700&status=done&style=none&taskId=u36cf6111-1815-482f-bad1-7a8ae74fea2&width=387)<br />初始时i=lo+1,lt=lo,gt=h,对a[i]与privot比较
    • a[i] < privot 交换a[lt],a[i] , lt++;i++;
    • a[i] > privot 交换a[gt],a[i],gt—;
    • a[i] = privot i++;
      1. void quicksort3way(vector<int>& a,int l,int h){
      2. if (l >= h) return ;//递归种植
      3. int i = l+1;lt=l,gt=h;
      4. int privot = a[l];
      5. while(i<=gt){
      6. if(a[i]<privot){
      7. swap(a[i++],a[lt++]);
      8. }else(a[i] > privot){
      9. swap(a[i],a[gt--])
      10. }else i++;
      11. }
      12. quicksort3way(a,l,lt-1);
      13. quicksort3way(a,gt+1,h);
      14. }
      3.三取样切分快排:因为上述的一般快排是把最左边的元素作为切分点(枢纽),然而可以选取部分小数组的中位数作为切分元素,但是计算中位数代价也许更高,这时可以用前三个元素(取样大小=3)的小数组中计算中位数而作为枢纽效果最好。 ```java //返回三取样切分元素索引 private static int ThreeMedianIndex(Comparable[] a,int lo,int hi) {
      1. //子数组少于3个元素时,第一个元素作为切分元素
      2. if((hi-lo+1)<3) return lo;
      3. //子数组有3个或以上元素时,取子数组前三个元素的中位数作为切分元素
      4. ////将原数组的前三个元素的索引作为新数组的值
      5. Integer[] b={lo,lo+1,lo+2};
      6. ////使用插入排序法排序新数组b,按原数组的值进行排序。排序后的结果是原数组中小中大值对应的索引
      7. for(int i=0;i<2;i++)
      8. for(int j=i+1;j>0;j--)
      9. if (less(a[b[j]],a[b[j-1]])) exch(b,j,j-1);
      10. return b[1];
      }

    private static int partition(Comparable[] a,int lo,int hi) { int i=lo,j=hi+1; int newVIndex=ThreeMedianIndex(a,lo,hi); exch(a,lo,newVIndex); Comparable v=a[lo]; while(true) { while(less(a[++i],v)) if(i==hi) break; while(less(v,a[—j])) if(j==lo) break;

    1. if(i>=j) break;
    2. exch(a,i,j);
    3. }
    4. exch(a,lo,j);
    5. return j;
    6. }
    1. 链接[链接](https://www.cnblogs.com/longjin2018/p/9860246.html)
    2. 3. 应用:寻找第K小的元素;如果是第K大,可以第n-K+1小来算。
    3. ```cpp
    4. int select(vector<int>&a,int k){
    5. int l = 0,r = n-1;
    6. while(l<r){
    7. int pos = partition(a,l,r);//pos是整个数组的索引
    8. if(pos == k-1) return a[pos];
    9. else if(pos > k-1) r=pos-1;
    10. else l=pos+1;
    11. }
    12. //当l >= r时,说明虽然没有找到pos=k-1但是a[]已经有序了,直接返回
    13. return a[k-1];
    14. }