前言
在算法的星空中,归并排序与快速排序如同双子星般闪耀。它们共享O(n log n)的时间复杂度,却用截然不同的方式演绎分治思想。作为前端开发者,理解这对"双子星"能让你在性能优化中游刃有余。
分治思想:算法的黄金法则
先明白分治思想是什么。
分治思想是解决复杂问题的核心策略,其本质是"化繁为简"。它包含三个关键步骤:
- 分解:将原问题拆分成多个规模更小的子问题
- 解决:递归地解决这些子问题(当子问题足够简单时,直接求解)
- 合并:将子问题的解组合成原问题的解
下面要讲的两种算法都离不开这个思想。
归并排序:稳定的编织者
归并排序通过递归拆分数组,再合并两个有序子数组。其稳定性(相同元素顺序不变)使其在需要保持原始顺序的场景中不可替代。
function mergeSort(arr) {
if (arr.length <= 1) return arr; // 递归终止条件
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid)); // 递归拆分左半
const right = mergeSort(arr.slice(mid)); // 递归拆分右半
return merge(left, right); // 合并有序子数组
}
function merge(arr1, arr2) {
let res = [], i = 0, j = 0;
while (i < arr1.length && j < arr2.length) {
// 比较元素,小的放入结果
if (arr1[i] <= arr2[j]) res.push(arr1[i++]);
else res.push(arr2[j++]);
}
// 拼接剩余元素(一个数组已遍历完)
return res.concat(arr1.slice(i)).concat(arr2.slice(j));
}
时间复杂度稳定O(n log n),空间复杂度O(n),稳定排序。
快速排序:高效的分区魔术师
快排通过选择基准元素(pivot)将数组分为左右两部分,递归排序。其原地操作特性大幅降低空间开销。
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[0]; // 基准元素(可优化为随机选择)
const left = [], right = [];
for (let i = 1; i < arr.length; i++) {
if (arr[i] < pivot) left.push(arr[i]); // 小于基准
else right.push(arr[i]); // 大于等于基准
}
return [...quickSort(left), pivot, ...quickSort(right)];
}
平均时间复杂度O(n log n),最坏O(n²)(可优化),空间复杂度O(log n),不稳定排序。
算法总结与对比
| 特性 | 归并排序 | 快速排序 |
|---|---|---|
| 时间复杂度 | 稳定O(n log n) | 平均O(n log n) |
| 空间复杂度 | O(n) | O(log n) |
| 稳定性 | ✅ 稳定 | ❌ 不稳定 |
| 适用场景 | 链表排序、稳定需求 | 数组排序、内存受限 |
当需要稳定排序时(如按时间排序的事件日志),归并排序更可靠; 但在内存受限的场景中(如大型数组),其原地操作特性使其成为多数语言标准库的选择。
实战练习:力扣75题 - 颜色分类
这道题完美演示了快排的分区思想——无需完整排序,一次遍历即可完成分类。
function sortColors(nums) {
let left = 0, right = nums.length - 1, i = 0;
while (i <= right) {
if (nums[i] === 0) {
// 0放左边:交换后i前进(左边换来的元素已处理)
[nums[left], nums[i]] = [nums[i], nums[left]];
left++;
i++;
} else if (nums[i] === 2) {
// 2放右边:交换后i不前进(右边换来的元素待处理)
[nums[right], nums[i]] = [nums[i], nums[right]];
right--;
} else {
i++; // 1直接跳过
}
}
}
这道题是快排分区思想的精简版。将数组分为<1(0)、=1(1)、>1(2)三部分,一次遍历完成,时间复杂度O(n),空间复杂度O(1)。
结语:算法选择的艺术
归并排序与快排不是非此即彼的抉择,而是算法智慧的双翼——当需要轻盈的效率时,快排如疾风般掠过;当需要沉稳的可靠时,归并排序如磐石般伫立。掌握这对双子星,便能在代码的海洋中精准定位,让每一次排序都成为问题的优雅解构,让每一段算法都闪耀着思维的光芒。
"算法不是代码的堆砌,而是问题的精准解构。" —— 归并与快排,正是这种解构思维的完美体现。