快速排序
题目:将数组按升序排列。例:
[9, 39, 7, 9, 10, 78, 3]→[3, 7, 9, 9, 10, 39, 78]。
1. 实现一:递归 + concat(思路清晰)
取中点作为基准(pivot),把小于基准的放进 left、不小于的放进 right,再递归拼接:
// [9 39 7 9 10 78 3] 进行升序排序
var sortArray = function(nums) {
if (nums.length <= 1) return nums;
let pivotIndex = Math.floor(nums.length / 2);
let mid = nums.splice(pivotIndex, 1)[0];
let left = [], right = [];
for (let i = 0; i < nums.length; i++) {
if (nums[i] < mid) left.push(nums[i]);
else right.push(nums[i]);
}
return sortArray(left).concat([mid], sortArray(right));
};
- 优点:思路最直接,每次都"取中分两半",类似归并的分裂模式;代码短。
- 缺点:
splice会改动原数组;额外分配left/right两个数组,空间占用O(n log n);稳定性依赖于实现。
2. 实现二:双哨兵原地交换(思路二)
用两个哨兵 i(左)、j(右)从两头往中间扫描。j 从右向左找第一个比基准(key = arr[from])小的元素,i 从左向右找第一个比基准大的元素,两者交换;当 i === j 时把基准换到 i 位置,再递归处理两侧。
/**
思路:两个哨兵 i、j;j 从右边找比基数小的,i 从左边找比基数大的,
然后交换两个目标元素的位置,直到 i=j,然后交换 i 和基数的位置,递归处理。
**/
function quick_sort(arr, from, to) {
let i = from; // 哨兵 i
let j = to; // 哨兵 j
let key = arr[from]; // 标准值
if (from >= to) { // 如果数组只有一个元素
return;
}
while (i < j) {
while (arr[j] > key && i < j) {
// 从右边向左找第一个比 key 小的数,找到或者两个哨兵相碰,跳出循环
j--;
}
while (arr[i] <= key && i < j) {
// 从左边向右找第一个比 key 大的数,找到或者两个哨兵相碰,跳出循环
// 这里的 = 号保证在本轮循环结束前,key 的位置不变,
// 否则跳出循环后交换 i 和 from 位置时,from 上的元素有可能已经不是 key
i++;
}
/**
代码执行到这里,
1、两个哨兵都找到了目标值。
2、j 哨兵找到了目标值。
3、两个哨兵都没找到(key 是当前数组最小值)。
**/
if (i < j) { // 交换两个元素的位置
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
arr[from] = arr[i]; //
arr[i] = key;
quick_sort(arr, from, i - 1);
quick_sort(arr, i + 1, to);
}
- 优点:原地交换,空间
O(log n)(递归栈);无需额外数组。 - 缺点:极端情况(已排序数组)下时间复杂度退化到
O(n²),生产中通常配合"三数取中"或随机化基准来缓解。
来源整理自:我的有道云笔记



