深入解析felipernb/algorithms.js中的归并排序实现
2025-07-09 04:55:35作者:柏廷章Berta
归并排序(Merge Sort)是一种经典的分治算法,本文将详细分析felipernb/algorithms.js项目中实现的归并排序算法,帮助读者深入理解其工作原理和实现细节。
归并排序算法概述
归并排序采用分治策略,将一个大问题分解为若干个小问题来解决。具体来说:
- 分解:将当前区间一分为二
- 解决:递归地对两个子区间进行排序
- 合并:将两个已排序的子区间合并成一个有序区间
该算法的时间复杂度为O(n log n),是一种稳定的排序算法。
核心实现分析
1. 合并函数(merge)
const merge = (a, b, comparator) => {
let i = 0;
let j = 0;
const result = [];
while (i < a.length && j < b.length) {
result.push(comparator.lessThan(a[i], b[j]) ? a[i++] : b[j++]);
}
return result.concat(i < a.length ? a.slice(i) : b.slice(j));
};
合并函数是归并排序的核心部分,负责将两个已排序的数组合并为一个有序数组:
- 使用双指针
i
和j
分别遍历两个数组 - 比较当前指针位置的元素,将较小的元素放入结果数组
- 当任一数组遍历完后,将剩余元素直接拼接至结果数组末尾
2. 排序主函数(mergeSortInit)
const mergeSortInit = (a, compareFn) => {
const comparator = new Comparator(compareFn);
return (function mergeSort(a) {
if (a.length > 1) {
const middle = a.length >> 1;
const left = mergeSort(a.slice(0, middle));
const right = mergeSort(a.slice(middle));
a = merge(left, right, comparator);
}
return a;
})(a);
};
主函数采用立即执行函数表达式(IIFE)实现:
- 初始化比较器:创建Comparator实例,支持自定义比较函数
- 递归终止条件:当数组长度≤1时直接返回(已有序)
- 分治策略:
- 使用右移操作
>> 1
快速计算中点(相当于除以2取整) - 递归处理左右子数组
- 合并已排序的子数组
- 使用右移操作
技术亮点
- Comparator设计:通过Comparator类封装比较逻辑,提高代码复用性和灵活性
- 位运算优化:使用
a.length >> 1
代替Math.floor(a.length/2)
,提升计算效率 - 函数式风格:采用纯函数实现,避免副作用,便于理解和测试
- 内存优化:使用slice创建子数组,避免修改原数组
使用示例
const mergeSort = require('./merge_sort');
// 默认排序(升序)
console.log(mergeSort([3, 1, 4, 2])); // 输出: [1, 2, 3, 4]
// 自定义比较函数(降序)
const descCompare = (a, b) => b - a;
console.log(mergeSort([3, 1, 4, 2], descCompare)); // 输出: [4, 3, 2, 1]
性能考虑
虽然归并排序的时间复杂度稳定为O(n log n),但需要注意:
- 空间复杂度:需要O(n)的额外空间
- 小数组优化:对于小规模数据,插入排序等简单算法可能更高效
- 递归深度:对于极大数组需要考虑栈溢出风险
总结
felipernb/algorithms.js中的归并排序实现展示了经典算法与现代JavaScript特性的结合,代码简洁高效,既保持了算法的核心思想,又充分利用了JavaScript的语言特性。通过分析这一实现,我们可以更好地理解分治策略在实际编码中的应用,以及如何编写清晰、高效的排序算法。