快速排序是一种分治算法。先把原数组拆分为两个子数组:较小元素、较大元素。然后递归地排序这些子数组。
具体步骤:
- 在数组中选择一个元素作为
基准
(pivot)。
- 所有小于
基准
的元素,都移到基准
的左边;所有大于基准
的元素,都移到基准
的右边。
- 对
基准
左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。
可以参考下图,每一次都取红色元素作为基准,执行以上步骤
图片来源 wikipedia
注:本文的两个解法,都属于快速排序
,不同之处在于选用了不同的基准值
。代码都是照抄网上的,然后手动加上了注释
版本一:algorithm-visualizer 的示例
基准值
取原数组的第一个元素
友情提示:可以在这里看这段代码的动画演示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
function partition(D, low, high) {
let i; // 小于基准值的下标
let j; // 大于基准值的下标
let s; // 基准值的下标
while (high > low) {
// 当数组只有 1 个元素时,结束递归
i = low;
j = high;
s = D[low]; // 基准值取原数组的第一个元素
while (i < j) {
// 遍历数组全部元素
while (s <= D[j]) {
// 从数组尾部开始,寻找小于等于基准值的元素
j--;
}
// 把右边的值放到左边
D[i] = D[j];
// 从数组头部开始,寻找大于基准值的元素
while (s > D[i] && i < j) {
i++;
}
// 把左边的值放到刚才右边的位置,完成“交换”
D[j] = D[i];
}
// 当 i = j 时,此时数组已排序完,把基准值放到结束的位置上
D[i] = s;
// 递归左边部分
partition(D, low, i - 1);
// 递归右边部分
low = i + 1;
}
}
function quicksort(D) {
partition(D, 0, D.length - 1);
}
// 调用
const a = [1,3,2,4,11,222,66,8,7,6]
quicksort(a);
console.log(a)
|
版本二:阮一峰博客上的示例
基准值
取原数组的中位数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
function quickSort(arr) {
// 当数组只有 1 个元素时,递归结束
if (arr.length <= 1) {
return arr;
}
// 取数组中位数作为基准值,并将其与原数组分离
var pivotIndex = Math.floor(arr.length / 2);
var pivot = arr.splice(pivotIndex, 1)[0];
var left = []; // 基准的左边:小于基准的元素
var right = []; // 基准的右边:大于基准的元素
for (var i = 0; i < arr.length; i++){
if (arr[i] < pivot) {
// 所有小于`基准`的元素,都移到`基准`的左边;
left.push(arr[i]);
} else {
// 所有大于`基准`的元素,都移到`基准`的右边。
right.push(arr[i]);
}
}
// 递归左、右子数组
return quickSort(left).concat([pivot], quickSort(right));
// return [...quickSort(left), pivot, ...quickSort(right)];
};
const result = quickSort([1,3,2,4,11,222,66,8,7,6])
|
复杂度
Name |
Best |
Average |
Worst |
Memory |
Stable |
Comments |
Quick sort |
n log(n) |
n log(n) |
n2 |
log(n) |
No |
Quicksort is usually done in-place with O(log(n)) stack space |
参考资料
快速排序可视化
阮一峰:快速排序(Quicksort)的 JavaScript 实现