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

排序算法有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() 还快了一点。(代码写错了,囧)

【前端也要学点算法】快速排序的JavaScript实现的更多相关文章

  1. 前端要不要学数据结构&算法

    我们都知道前端开发工程师更多偏向 DOM 渲染和 DOM 交互操作,随之 Node 的推广前端工程师也可以完成服务端开发.对于服务端开发而言大家都觉得数据结构和算法是基础,非学不可.所以正在进行 No ...

  2. 【前端也要学点算法】 归并排序的JavaScript实现

    前文我们了解了快速排序算法的实现,本文我们来了解下另一种流行的排序算法-归并排序算法. 我们先来回顾下快排.快排的核心是找出一个基准元素,把数组中比该元素小的放到左边数组,比该元素大的放到右边数组,如 ...

  3. 做acm 需要学的算法

    做acm 需要学的算法 转一个搞ACM需要的掌握的算法.  要注意,ACM的竞赛性强,因此自己应该和自己的实际应用联系起来.  适合自己的才是好的,有的人不适合搞算法,喜欢系统架构,因此不要看到别人什 ...

  4. 前端开发周报: CSS 布局方式方式与JavaScript数据结构和算法

    前端开发周报:CSS 布局方式与JavaScript动画库 1.常见 CSS 布局方式详见: 一些常见的 CSS 布局方式梳理,涉及 Flex 布局.Grid 布局.圣杯布局.双飞翼布局等.http: ...

  5. web前端入坑第二篇:web前端到底怎么学?干货资料! 【转】

    http://blog.csdn.net/xllily_11/article/details/52145172 版权声明:本文为博主[小北]原创文章,如要转载请评论回复.个人前端公众号:前端你别闹,J ...

  6. 《Java算法》排序算法-快速排序

    排序算法-快速排序: /** * 给定一个数组:按照从小到大排序. * 思路: * 1. 获取第一个数放入临时变量data,将大于data的数放右边,小于data的数放在左边. * 2. data左边 ...

  7. 数据结构和算法(Golang实现)(25)排序算法-快速排序

    快速排序 快速排序是一种分治策略的排序算法,是由英国计算机科学家Tony Hoare发明的, 该算法被发布在1961年的Communications of the ACM 国际计算机学会月刊. 注:A ...

  8. 第十四章 web前端开发小白学爬虫

    老猿从事IT开发快三十年了,接触互联网也很久了,但自己没有做过web前端开发,只知道与前端开发相关的一些基本概念,如B/S架构.html标签.js脚本.css样式.xml解析.cookies.http ...

  9. 使用 js 实现十大排序算法: 快速排序

    使用 js 实现十大排序算法: 快速排序 QuickSort 快速排序 /** * * @author xgqfrms * @license MIT * @copyright xgqfrms * @c ...

随机推荐

  1. BIEE11G常用函数及使用说明

    BIEE常用函数使用手册 1.AGGREGATE AT 此函数根据指定的级别聚合列.使用AGGREGATE AT 可确保始终在关键字AT 之后指定的级别执行度量聚合,而无论WHERE 子句如何. 语法 ...

  2. mysql-4 数据检索(2)

    用通配符进行过滤 like操作符  %通配符   %可以匹配任意字符 SELECT prod_id , prod_name FROM products WHERE prod_name LIKE 'je ...

  3. 创建一个Point类,有成员变量x,y,方法getX(),setX(),还有一个构造方 法初始化x和y。创建类主类A来测试它

    package com.hanqi.test; public class Point { private int x; private int y; Point(int xx,int yy) { x= ...

  4. 将表里的数据批量生成INSERT语句的存储过程 继续增强版

    文章继续 桦仔兄的文章 将表里的数据批量生成INSERT语句的存储过程 增强版 继续增强... 本来打算将该内容回复于桦仔兄的文章的下面的,但是不知为何博客园就是不让提交!.... 所以在这里贴出来吧 ...

  5. c# UpdateLayeredWindow异形窗口

    #region UpdateLayeredWindow #region 重写窗体的 CreateParams 属性 protected override CreateParams CreatePara ...

  6. sql server 有关锁的视图说明 syslockinfo

    ransact-SQL 参考   https://msdn.microsoft.com/zh-cn/library/ms179881.aspx  syslockinfo 包含有关所有已授权.正在转换和 ...

  7. text-transform设置单词首字母大写

    text-transform 一.语法   text-transform 主要用于设置文本的大小写. text-transform有5个值,分别如下: none. 默认值. capitalize. 文 ...

  8. 烂泥:linux文件同步之rsync学习(一)

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 这几天刚好有空就打算开始学习linux下的文件同步软件rsync,在学习rsync时,我们可以分以下几个步骤进行: 1. rsync是什么 2. rsy ...

  9. (转)Yii的组件机制之一:组件基础类CComponent分析

    Yii的组件机制 组件机制,是Yii整个体系的思想精髓,在使用Yii之前,最应该先了解其组件机制,如果不了解这个机制,那么阅读Yii源代码会非常吃力.组件机制给Yii框架赋予了无穷的灵活性和可扩展性, ...

  10. AC日记——画矩形 1.5 42

    42:画矩形 总时间限制:  1000ms 内存限制:  65536kB 描述 根据参数,画出矩形. 输入 输入一行,包括四个参数:前两个参数为整数,依次代表矩形的高和宽(高不少于3行不多于10行,宽 ...