作为算法目录下的第一篇博文,快速排序那是再合适不过了。作为最基本最经典的算法之一,我觉得每个程序员都应该熟悉并且掌握它,而不是只会调用库函数,知其然而不知其所以然。

排序算法有10种左右(或许更多),耳熟能详的冒泡排序、选择排序都属于复杂度O(n^2)的“慢”排,而快排的复杂度达到了O(nlongn),快排是怎么做到的呢?跟着楼主一步步探索快排的奥秘吧。

:如没有特殊说明,本文的快速排序都是针对数组,且排序结果从小到大。


快速排序其实就三步:

  1. 在需要排序的数组中,任选一个元素作为“基准”
  2. 将小于“基准”和大于“基准”的元素分别放到两个新的数组中,等于“基准”的元素可以放在任一数组
  3. 对于两个新的数组不断重复第一步第二步,直到数组只剩下一个元素,这时step2的两个数组已经有序,排序结果也很容易得到了(leftArray+基准元素+rightArray)

以数组[1, 2, 5, 4, 3]举例,第一次排序,找个基准,基准可以是数组的任意元素,为了方便说明,可以选择第一个元素,这里我以中间元素举例。于是找到5为基准,小于5和大于5的分别放到两个新的数组中,等于5的可以放到任意一边,第一次排序后,得到结果:

[1, 2, 4, 3] 5 []

然后两个新的数组再次进行如上排序(例子中一个数组是空的,so只需进行一个数组的排序),我们可以很高兴地发现,如果左右两个数组分别排序完后,三个数组按顺序concat后就是我们要的结果了。

再看数组[1, 2, 4, 3],选取中间元素4作为基准,排序后得到:

[1, 2, 3] 4 [] .. 5 []

对于长度大于1的数组继续进行操作:

[1] 2 [3] 4 [] 5 []

great!排序完毕!


接着我们用代码实现过程。

首先定义一个名为quickSort的函数,参数是一个需要排序的数组:

function quickSort(a) {

}

如果数组长度小于1,那么就不用进行排序了,直接返回数组:

function quickSort(a) {
if (a.length <= 1) return a;
}

否则,我们取数组的中间元素,将数组中小于等于中间元素的元素放到left数组,大于中间元素的元素放到right数组:

function quickSort(a) {
if (a.length <= 1) return a; var mid = ~~(a.length / 2)
, midItem = a.splice(mid, 1)[0]
, left = []
, right = []; a.forEach(function(item) {
if (item <= midItem)
left.push(item);
else
right.push(item);
});
}

我们知道,如果left数组和right数组都已经排序完毕了,那么直接返回left+midItem+right组成的数组就大功告成了。但是left和right数组是无序的,怎么办?我们定义的quickSort()函数就是用来排序的,递归调用即可:

function quickSort(a) {
if (a.length <= 1) return a; var mid = ~~(a.length / 2)
, midItem = a.splice(mid, 1)[0]
, left = []
, right = []; a.forEach(function(item) {
if (item <= midItem)
left.push(item);
else
right.push(item);
}); var _left = quickSort(left)
, _right = quickSort(right); return _left.concat(midItem, _right);
}

这样才真正的大功告成了,快速排序算法是不是也不那么难?

参考:阮一峰老师的快速排序(Quicksort)的Javascript实现


2016-10-13 补:

如果需要排序的数组有大量重复元素,可以用基于三向切分的快速排序大幅度提高效率。

基础的快排,每一次递归,我们将数组拆分为两个,递归出口是数组长度为 <=1。思考这样一个场景,递归过程中某个数组为 [1, 1, 1, 1, 1, 1, 1, 1],如果是原始的快排,还需要继续递归下去,实际上已经不需要。所以我们可以用三向切分,简单地说就是将数组切分为三部分,大于基准元素,等于基准元素,小于基准元素。

我们可以设置一个 mid 数组用来保存等于基准元素的元素集合,以前取的基准元素是数组中间位置的元素,其实任意一个即可,这里选了最后一个,比较方便。

function quickSort(a) {
if (a.length <= 1) return a; var last = a.pop()
, left = []
, right = []; a.forEach(function(item) {
if (item <= last)
left.push(item);
else
right.push(item);
}); var _left = quickSort(left)
, _right = quickSort(right); return _left.concat(last, _right);
} function quickSort3Way(a) {
if (a.length <= 1) return a; var last = a.pop()
, left = []
, right = []
, mid = [last]; a.forEach(function(item) {
if (item < last)
left.push(item);
else if (item > last)
right.push(item);
else
mid.push(item);
}); var _left = quickSort3Way(left)
, _right = quickSort3Way(right); return _left.concat(mid, _right);
} // test cases
// 最好 shuffle 下
var arr = [];
for (var i = 0; i < 1000; i++)
for (var j = 0; j < 10; j++) // 包含大量重复元素
arr.push(i); console.log(console.time('quickSort'));
quickSort(arr.concat()); // quickSort: 3407.842ms
console.log(console.timeEnd('quickSort')); console.log(console.time('quickSort3Way'));
quickSort3Way(arr.concat()); // quickSort3Way: 215.705ms
console.log(console.timeEnd('quickSort3Way')); console.log(console.time('v8 sort'));
arr.concat().sort(function(a, b) {
return a - b;
}); // v8 sort: 10.126ms
console.log(console.timeEnd('v8 sort'));

测试中的这个 case,经过三向切分的快排的效率甚至比 v8 的 Array.prototype.sort() 还快了一点。(代码写错了,囧)


2017-03-21: 几天前的面试,面试官问我能不能用一行代码写快排,我回忆了下,感觉不行,面试官提示了用 filter 方法,今天试了下,还真是可以 ...

function quickSort(a) {
return a.length <= 1 ? a : quickSort(a.slice(1).filter(item => item <= a[0])).concat(a[0], quickSort(a.slice(1).filter(item => item > a[0])));
}

快速排序 JavaScript 实现的更多相关文章

  1. 快速排序 javascript实现

    Quicksort(快速排序) 是由 东尼·霍尔 所发展的一种排序. 它比其他的Ο(n log n)算法更快, 因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来.当然, ...

  2. 快速排序javascript实现

    快速排序基本思想: 以升序为例 数组arr,数组个数n; 1.选取一个待排序的元素.一般选第一个位置作为基准值temp=arr[0]. 2.选取带排序数组的两端元素的位置作为哨兵的位置,左端为哨兵i, ...

  3. 快速排序——JavaScript实现

    基本原理: 1.从一个数组中任意挑选一个元素作为中轴元素: 2.将剩下的元素以中轴元素作为比较的标准,将小于等于中轴元素的放到中轴元素的左边,将大于中轴元素的放到中轴元素的右边: 3.以当前中轴元素的 ...

  4. JavaScript实现10大算法可视化

    参考博客: https://www.cnblogs.com/Unknw/p/6346681.html#4195503 十大经典算法 一张图概括: 名词解释: n:数据规模 k:“桶”的个数 In-pl ...

  5. JS的十大经典算法排序

    引子 有句话怎么说来着: 雷锋推倒雷峰塔,Java implements JavaScript. 当年,想凭借抱Java大腿火一把而不惜把自己名字给改了的JavaScript(原名LiveScript ...

  6. JS家的排序算法

    由于浏览器的原生支持(无需安装任何插件),用JS来学习数据结构和算法也许比c更加便捷些.因为只需一个浏览器就能啪啪啪的调试了.比如下图我学习归并排序算法时,只看代码感觉怎么都理解不了,但是结合chro ...

  7. js排序算法汇总

    JS家的排序算法   十大经典算法排序总结对比 一张图概括: 主流排序算法概览 名词解释: n: 数据规模k:“桶”的个数In-place: 占用常数内存,不占用额外内存Out-place: 占用额外 ...

  8. JS中常见排序算法详解

    本文将详细介绍在JavaScript中算法的用法,配合动图生动形象的让你以最快的方法学习算法的原理以及在需求场景中的用途. 有句话怎么说来着: 雷锋推倒雷峰塔,Java implements Java ...

  9. js 算法排序总结

    1.冒泡排序JavaScript代码实现: function bubbleSort(arr) { var len = arr.length; for (var i = 0; i < len; i ...

随机推荐

  1. git中 vi/vim的命令

    一.vi & vim 有两种工作模式: 1.命令模式:接受.执行 vi操作命令的模式,打开文件后的默认模式: 2.编辑模式:对打开的文件内容进行 增.删.改 操作的模式: 在编辑模式下按下ES ...

  2. 线特征---LSD算法(二)

    上一节介绍了一些资源和实验结果,这节主要是介绍LSD算法理论. 直线段检测算法---LSD:a Line Segment Detector LSD的核心是像素合并于误差控制.利用合并像素来检测直线段并 ...

  3. centos 命令学习

    关机&重启 shutdown -h 10          #计算机将于10分钟后关闭,且会显示在登录用户的当前屏幕中 shutdown -h now       #计算机会立刻关机 shut ...

  4. 9.28 h5日记

    9.28 1.transparent 透明的 颜色 2.placeholder 提示语 在input中使用 跟velue不同 3.写页面需要注意的 (1)页面一定要有层次,分清层次 (2)保证元素模块 ...

  5. 为Linux虚拟机设置网络

    安装虚拟机的时候为了使用方便我们除了需要设置静态ip为了能够让虚拟机也能够上网我们需要设置虚拟机网络 当然也可以使用虚拟机和主机共享上网,这个比较简单,这里就不说了,现在我们来通过桥接的方式为虚拟机设 ...

  6. mybatis进阶--mapper输入映射和输出映射

    我们知道,mapper.xml是我们配置操作数据库的sql语句的地方.其中每个sql语句对应着一个方法,每个方法都有自己的输入输出参数类型.那么这些类型都是怎么配置的呢?今天我们来一起学习下. 输入映 ...

  7. Tomcat假死的原因及解决方案

    服务器配置:linux+tomcat 现象:Linux服务器没有崩,有浏览器中访问页面,出现无法访问的情况,没有报4xx或5xx错误(假死),并且重启tomcat后,恢复正常. 原因:tomcat默认 ...

  8. poj_1979(dfs)

    Red and Black There is a rectangular room, covered with square tiles. Each tile is colored either re ...

  9. http协议(一)一些基础知识

    当我们在浏览器的地址栏中输入网址,然后点击回车,接着,浏览器就会呈现出我们需要的web界面,那么,这个界面是怎么产生的? web的界面是根据我们输入的URL(网址.地址),浏览器从服务器端获取对应的文 ...

  10. 20172306《Java程序设计》第五周学习总结

    20172306 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 第五章主要学习了if以及while的语句的运用 运算符:== 代表相等,是两个之间的内存地址 ...