算法学习in js:快速排序

2,374 阅读3分钟

前言

快速排序应该是排序中最出名的算法了,也可能是应用最广泛的排序算法了。他之所以流行,是因为实现简单,同时适用于各种数据,且一般比其他排序算法要快。

快排原理

快排是对冒泡排序的一种改进,它之所以快是因为一次交换能改变多个逆序对,而冒泡排序只能改变一个逆序对。

而快排的基本思想即是:通过一趟排序将欲排序的数据分割成两部分,其中一部分数据比另一部分所有数据都要小。接着继续对这两部分数据进行快速排序。则是分治思想的体现。

下面我们用图例来看看一趟排序

此过程如下:

  1. 选定数组a[lo]作为切分元素,同时令i = lo, j = hi,即对应lo、hi位置
  2. i指针从左向右扫描,遇见>v的数时暂停;j指针从右向左扫描,遇见<v的数时暂停。i、j指针指向数字交换;重复2流程,直至i >= j。
  3. 当2流程结束时,此时j指向小于切分元素的数字,进行交换。此时切分元素已位于最终位置,切分元素左侧元素均小于切分元素,右侧元素均大于切分元素
  4. 对却分后的子数组重复上述流程:sort(array, lo, j - 1);sort(array, j + 1, hi)。直至递归结束

代码实现

核心代码sortpartition,sort用于递归调用,partition用于切分

  sort(array, lo, hi) {
    if(hi <= lo ){
      return ;
    }
    const j = this.partition(array, lo, hi);
    this.sort(array, lo, j - 1);
    this.sort(array, j + 1, hi);
  }
  partition(array, lo, hi){
    let i = lo;
    let j = hi + 1;
    const ele = array[lo];//切分元素
    while(true){
      while(this.less(array[++i], ele)){//左向右扫描
        if(i === hi){
          break;
        }
      }
      while(this.less(ele, array[--j])){//右向左扫描
        if(j === lo){
          break;
        }
      }
      if(i >= j){
        break;
      }
      this.exch(array, i ,j);//左右元素交换
    }
    this.exch(array, lo, j);//切分元素交换
    return j;
  }

CodePen打开

性能特点

主要优点:

  • 实现简单
  • 一般情况下比其他排序算法快得多
  • 长度为N的数组排序时间为NlgN
  • 内循环比大多数排序算法都要短小

主要缺点:

  • 脆弱,在切分不平衡时可能导致极低的性能。比如倒序变升序

总结

以上的快排是最基础的版本。针对不同的场景,其实还有很多的改进空间使得性能提升,比如:

  • 对于小数组可在快排中切换至插入排序
  • 对于含有大量重复元素的数组,比如生日、性别,可将数组切分为三部分,分别对应小于、等于、大于切分元素的数组元素

以上内容也主要是学习于《算法(第4版)》一书中,感兴趣的同学可以查阅。

参考资料

《算法(第4版)》
快速排序算法详解(原理、实现和时间复杂度)