sort 函数的工作原理
通过比较数组中两个元素的大小来决定它们的位置。比较函数 (compare) 会被传入两个参数 a 和 b,它的返回值决定了这两个元素的顺序:
如果返回值小于 0,那么 a 会被排在 b 之前。 如果返回值等于 0,那么 a 和 b 的顺序保持不变。 如果返回值大于 0,那么 a 会被排在 b 之后。
用冒泡排序实现 sort 函数
function myBubbleSort(arr, compare) {
const n = arr.length;
// 外循环:未排序区间 [0, i]
for (let i = n - 1; i > 0; i--) {
// 内循环:将未排序区间的最大元素换到区间最右端
for (let j = 0; j < i; j++) {
// 使用比较函数来决定元素是否需要交换位置
if (compare(arr[j], arr[j + 1]) > 0) {
// 交换元素
const tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
return arr;
}
// 比较函数示例:升序排序
function ascending(a, b) {
return a - b;
}
// 使用自定义排序函数对数组进行排序
const arr = [5, 3, 8, 4, 2];
const sortedArr = myBubbleSort(arr, ascending);
console.log(sortedArr); // 输出: [2, 3, 4, 5, 8]
用快速排序实现 sort 函数
快速排序原理
快速排序是一种“分而治之”的算法。它通过选择一个“基准”(pivot)元素,将数组分成两个子数组,分别是小于基准的部分和大于基准的部分。然后递归地对这两个子数组进行排序,最终将排序好的子数组合并起来。
function myQuickSort(arr, compare) {
// 如果数组只有一个元素或为空,则直接返回
if (arr.length <= 1) {
return arr;
}
// 选择第一个元素作为基准,也可以任选一个元素
const pivot = arr[0];
// 创建两个空数组,分别存放比基准小、比基准大的元素
const left = [];
const right = [];
// 从第二个元素开始遍历数组(因为pivot是arr[0]),将元素按照大小放入left或right数组中
for (let i = 1; i < arr.length; i++) {
// if (i === pivot) continue; // 如果不是arr[0],i从0开始,需要跳过基准元素
if (compare(arr[i], pivot) < 0) {
left.push(arr[i]); // 小于基准的元素放在左边
} else {
right.push(arr[i]); // 大于或等于基准的元素放在右边
}
}
// 递归地对left和right数组进行排序,并将结果合并成一个有序数组并返回
return myQuickSort(left, compare).concat(pivot, myQuickSort(right, compare));
// return [...myQuickSort(left, compare), pivot, ...myQuickSort(right, compare)];
}
// 比较函数示例:升序排序
function ascending(a, b) {
return a - b;
}
// 使用自定义快速排序函数对数组进行排序
const array = [5, 3, 8, 4, 2];
const sortedArray = myQuickSort(array, ascending);
console.log(sortedArray); // 输出: [2, 3, 4, 5, 8]
代码解释
递归基准: 当数组的长度小于等于 1 时,直接返回该数组,因为它已经是有序的。
选择基准: 我们选择数组中间的元素作为基准(pivot)。
分区操作: 遍历数组,基于比较函数的结果,将小于基准的元素放入 left 数组,大于等于基准的元素放入 right 数组。
递归排序: 递归地对 left 和 right 数组进行排序,并将结果与基准元素合并起来,形成一个新的排序数组。
为什么快速排序更快?
快速排序在平均情况下的时间复杂度为 O(n log n),这使它比冒泡排序的 O(n^2) 更高效。它通过“分而治之”的策略,将问题规模减小,从而在大多数情况下表现出更好的性能 ,特别适合处理较大规模的数组。