快速排序是一个时间复杂度为O(nlogn)的算法,可以快速处理百万数量级数据。
1.主要思想
第一步:找到一个基准值(v)
第二步:进行划分,使得比基准值小的值在基准值(v)左边,比基准值大的值在基准值(v)右边
第三步:对划分好的左右两部分,再重复进行一、二步
2.举个栗子
假设有一个数组[34,21,56,43,33,89,3]
第一步:选择数组的第一个值为"基准值"
第二步: 按照顺序,将每个元素与"基准值"进行比较,形成两个子集,一个"小于34",另一个"大于等于34"
第三步:对划分好的左右两部分,再重复进行一、二步
3.具体实现
在具体实现中会出现一个swap
方法,主要用于把数组内的两个位置进行交换,
function swap(arr, a, b) {
if (a===b) return
let c = arr[a];
arr[a] = arr[b];
arr[b] = c;
}
一、定义三个函数
quickSort函数: 主要用于被外部调用;参数为数组;无返回值;
sort函数:主要用于递归处理数组;参数为数组和要进行划分的数组的起始下角标;无返回值;
_partation函数:用于处理划分数组;参数为数组和要进行划分的数组的起始下角标;返回值为“基准值”所在位置
function quickSort (arr) {}
function sort (arr, l, r) {}
function _partation (arr, l, r) {}
二、quickSort函数
首先,检查数组元素的个数,小于等于1,返回;
function quickSort (arr) {
+ let n = arr.length
+ if (n<=1) return
}
其次,进行数组的划分
function quickSort (arr) {
let n = arr.length
if (n<=1) return
+ _partation(arr, 0, n-1)
}
三、sort函数
首先,确定递归的停止条件,当数组的左角标大于等于右角标时,说明已经没有再进行数组划分的必要了,直接返回
function sort (arr, l, r) {
+ if (l>=r) return
}
其次,进行数组的划分,得到“基准值”的位置
function sort (arr, l, r) {
if (l>=r) return
+ let j = _partation(arr, l, r)
}
最后,再次调用sort函数,继续进行“基准值”左右两个数组划分; [l…j-1]为小于“基准值”的元素集合
function sort (arr, l, r) {
if (l>=r) return
let j = _partation(arr, l, r)
+ sort(arr, l, j-1)
+ sort(arr, j+1, r)
}
四、_partation函数
首先,定义一些变量用来记录关键信息值
v: 代表“基准值”,这里暂定数组第一个元素为“基准值”,即l值;
lpos: 代表小于“基准值”部分的最后一个元素的下角标;
function _partation (arr, l, r) {
+ let lpos = l;
+ let v = arr[l];
}
然后,按照顺序,与“基准值”进行比较,小于“基准值”的元素放到lpos
的下一个位置,并且lpos++
,让lpos
继续代表小于“基准值”部分的最后一个元素的下角标,直到循环结束
function _partation (arr, l, r) {
let lpos = l;
let v = arr[l];
+ for (let i = l+1;i<=r;i++){
+ if (arr[i]<v) {
+ swap(arr, i, lpos+1);
+ lpos++;
+ }
+ }
}
接着,循环完成。把lpos
位置上的元素和l上的元素(即基准值)进行交换,实现lpos
位置之前的元素均小于“基准值”,并且返回“基准值”的位置
function _partation (arr, l, r) {
let lpos = l;
let v = arr[l];
for (let i = l+1;i<=r;i++){
if (arr[i]<v) {
swap(arr, i, lpos+1);
lpos++;
}
}
+ swap(arr, l, lpos );
+ return lpos;
}
五、优化
1.取基准值的优化
在数组近乎有序时候,基准值从第一个元素开始取,也就是会获得一个最小值,在它划分时会出现,下面的情况,
例如:
…
原本最好可以划分为3层,但是这里却变成了7层,算法会退化成O(n^2)级别。
优化方法:基准值变成一个随机数,这样在数据足够大时,每次取到最小值的概率会变得非常小。
function _partation (arr, l, r) {
let lpos = l;
+ let randomIndex = parseInt(Math.random() * (r - l + 1) + l);
+ swap(arr, l, randomIndex);
let v = arr[l];
for (let i = l+1;i<=r;i++){
if (arr[i]<v) {
swap(arr, i, lpos+1);
lpos++;
}
}
swap(arr, l, lpos );
return lpos;
}
2.当要划分的数组,个数小到一定程度的时候,可以借助插入排序,完成数组排序。
插入排序也是个神奇的算法,以后会总结,这里就先不说了。
这是本人第一次总结所学知识,行文思路不是很好,本文内容有借鉴阮一峰老师在写快速排序分析的思路,写的不好,望大家海涵,内容有误的地方,望大家指出。
参考资料:
https://github.com/liuyubobobo/Play-with-Algorithms
http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html