一文教你理解排序算法——快速排序

1,093 阅读3分钟

一文教你理解排序算法——快速排序

介绍

排序想必大家都不会陌生,在日常的业务场景当中,排序是一个很常见的功能,按照分数排序、按照排名排序等等,数组对于排序也很贴心的给出了相应的高阶函数sort()。相信大家在日常开发当中用的肯定不少,不过俗话说得好,授人以鱼不如授人以渔。我们不仅要会使用 api,在面对特殊的业务场景的时候,我们也可以自己上阵。本文我们就来探究一下快速排序是实现。

快速排序

这是图片

快速排序算法由于它的方便和高效在实践中得到广泛的应用。快速排序在平均情况下的时间复杂度为 O(nlogn),在这个意义下,快速排序算法是平均情况下效率最高的算法之一。

快速排序伪码实现

算法 Quicksort 输入:数组 A[p,...,r],1<=p<=r<=n //p,r 分别为数组 A 的首元素和尾元素的下标 输出:从 A[p]到 A[r]按照递增顺序排好序的数组 A

  1. if p < r
  2. then q <- Partition(A,p,r)
  3. A[p] <-> A[q]
  4. Quicksort(A,p,q-1)
  5. Quicksort(A,q+1,r)

算法 Partition 输入:数组 A[p,r] 输出:j,A 的首元素在排好序的数组中的位置

  1. x <- A[p]
  2. i <- p
  3. j <- r
  4. while true do
  5.           repeat j <- j - 1;
    
  6.           until A[j] <= x
    
  7.           repeat i <- i + 1
    
  8.           until A[i] > x
    
  9.           if i < j
    
  10.   then A[i] <-> A[j]
    
  11.   else return j
    

原理解析

快速排序的思想是先利用首元素为标准,从前往后寻找第一个比首元素大的元素,从后往前找第一个比首元素小的元素。在算法当中,这种思想称为分治策略分治策略使用的场景很多,比如二分查找,归并排序,汉诺塔等等。这些以后我们有时间再来实现。上面的伪码当中,先判断首元素下标是否比尾元素下标大,如果大于或等于的话,说明数组已经全部排序完成。然后通过Partition来获取首元素在排序好的数组当中的位置,然后和首元素和那个位置上的元素进行交换,这样首元素就被插入到合适的地方。现在我们已经排序好了一个数,以这个数为分割点,数组一分为二,前面的都比分割点小,后面的都比分割点大,因此,我们使用递归来不断地对这两部分进行排序。而Partition算法实现的是获取首元素在排好序当中的位置下标。首先获取首元素,作为标准。然后把首元素下标赋值给 i,把尾元素下标赋值给 j。通过双层 while 来不断比较。先从后往前扫描数组 A,找到第一个比首元素小的数,这是 A[j]后面的数都比首元素大。然后从前往后扫描数组 A,找到第一个比首元素大的数 A[i],然后进行交换,这样后面比首元素小的数就到了前面,前面比首元素大的数就到了后面。接着继续寻找,直到 i>j 时,整个数组都被扫描完成。然后返回 j,这时候 j 就是首元素我们应该在的下标。

Java 实现

package com.sort;

public class Quicksort {
    //排序的数组
    int array[];
    //首元素下标
    int low;
    //尾元素下标
    int height;
    //初始化类
    public Quicksort(int array[]){
        this.array = array;
        this.low = 0;
        this.height = array.length - 1;
        quicksort(this.array,this.low,this.height);
    }
    //进行排序
    public void quicksort(int array[],int low,int height){
        int q,temp;
        if(low < height){
            //获取位置
            q = partition(array,low,height);
            //将首元素插入到合适的位置
            temp = array[low];
            array[low] = array[q];
            array[q] = temp;

            //使用递归,将以标准元素分出来的区域进行排序
            quicksort(array,low,q-1);
            quicksort(array,q+1,height);
        }
    }

    //排序
    public int partition(int array[],int low,int height){
        //先将首元素设为对比标准
        int normalEle = array[low];
        //将首元素给i,同时跳过第一个元素不比较
        int i = low + 1;
        //将尾元素给j
        int j = height;
        //定义临时交换变量
        int temp;

        //开始寻找标准元素所处的位置
        while (true){
            //从头开始遍历,寻找第一个比标准大的数,同时首元素下标不能比尾元素大
            while (array[i] <= normalEle){
                i++;
            }
            //从尾开始遍历,寻找第一个比标准小的数,同时首元素下标不能比尾元素大
            while (array[j] > normalEle){
                j--;
            }

            if(i < j){
                //将首元素和尾元素所找到的元素进行交换
                temp = array[i];
                array[i] = array[j];
                array[j] = temp;
            }else {
                //遍历完一轮,返回本轮找到的标准元素所在的下标
                return j;
            }
        }
    }
}

TS 实现

function quick(array: number[]) {
  //不是数组字节返回
  if (!Array.isArray(array)) {
    return;
  }
  //首元素下标
  let low: number = 0;
  //尾元素下标
  let height: number = array.length - 1;

  quickSort(array, low, height);
}

function quickSort(array: number[], low: number, height: number) {
  let swap: number;
  let temp: number;
  if (low < height) {
    //获取标准元素应该在数组当中的下标
    swap = partition(array, low, height);

    temp = array[low];
    array[low] = array[swap];
    array[swap] = temp;

    quickSort(array, low, swap - 1);
    quickSort(array, swap + 1, height);
  }
}

function partition(array: number[], low: number, height: number): number {
  //首元素下标,跳过第一个首元素
  let i: number = low + 1;
  //尾元素下标
  let j: number = height;
  //比对标准
  let normalEle: number = array[low];
  //交换变量
  let temp: number;

  while (true) {
    //从前往后寻找第一个比标准元素大的元素
    while (array[i] < normalEle) {
      i++;
    }

    //从后往前找,寻找第一个比标准元素小的元素
    while (array[j] > normalEle) {
      j++;
    }

    //如果i<j,那么两个元素交换
    if (i < j) {
      temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    } else {
      return j;
    }
  }
}

export default quick;