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

排序算法有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. Three.js粒子特效,shader渲染初探(一篇非常详细的介绍)

    Three.js粒子特效,shader渲染初探 转载来源:https://juejin.im/post/5b0ace63f265da0db479270a 这大概是个序 关于Three.js,网上有不多 ...

  2. TotoiseSVN 使用参考文章

    SVN使用教程总结 http://www.cnblogs.com/armyfai/p/3985660.html TotoiseSVN的基本使用方法 http://www.cnblogs.com/xil ...

  3. c# 使用ssh.net 上传文件

    在ssh.net 客户端实例下无法普通用户切换到su root  超级用户,原因是tty 的不支持,具体原因未查, 连接时用超级用户,问题解决 使用ssh.net  能实现远程命令,  使用其中的sf ...

  4. C#百度图片识别API调用返回数据包解析

    百度图片识别api接口 public static JObject GeneralBasic(string apikey,string secretkey,string path) { var cli ...

  5. What is MaxiSys Pro MS908P Software Advantage

    The Autel Maxisys Pro Diagnostic Platform is an evolutionary smart solution for specialized automoti ...

  6. 爬取掌阅app免费电子书数据

    主要介绍如何抓取app数据及抓包工具的使用,能看到这相信你已经有爬虫基础了 编不下去了,主要是我懒,直接开干吧! 一.使用环境和工具 windows + python3 + Jsonpath + Ch ...

  7. 从1~N中任选出三个数,最小公倍数最大

    已知一个正整数N,问从1~N中任选出三个数,它们的最小公倍数最大可以为多少. 当n为奇数:n.n-1.n-2这是三个最大数,并且它们两两互质.因为连续的奇.偶.奇,互质.连续的两个数互质是因为它们的公 ...

  8. 多维数组sorted函数的用法

    对某一个位置排列 l=[[1,5,7,9],[5,10,6,11],[4,2,1,4]] newlist=sorted(l,key=lambda iterm : iterm[0],reverse=Tr ...

  9. [AI]AI章2 框架比较

    深度学习框架比较 神经网络一般包括:训练,测试两大阶段.训练:就是把训练数据(原料)和神经网络模型:如AlexNet.RNN等“倒进” 神经网络训练框架例如cafffe等然后用 CPU或GPU(真火) ...

  10. Java中的逆变与协变 很直接不饶弯的讲出来了

    ```java 协变 extends只能new 辈分比自己低的家伙 List<? extends Number> list001 = new ArrayList<Integer> ...