javascript - 快速排序算法比较
问题描述
不明白为什么我的排序算法落在了 50000 个数字的数组上
const myquicksort2 = (sortedArray) => {
const sort = (start, end) => {
if (end - start <= 1) return;
let pivot = sortedArray[end - 1];
let pivotIndex = end - 1;
for (let i = start; i < end; i++) {
if (sortedArray[i] > pivot && i < pivotIndex) {
let temp = sortedArray[i];
sortedArray[i] = pivot;
sortedArray[pivotIndex] = temp;
pivot = temp;
}
}
sort(start, pivotIndex);
sort(pivotIndex + 1, end);
};
sort(0, sortedArray.length);
return sortedArray;
};
我在排序时没有创建新数组并更改枢轴值,但第二个示例在 50000 上没有失败,并且在https://jsperf.com/sorting12389上表现更好
function sort(arr) {
if (arr.length > 1) {
const medium = arr[0];
const leftPart = [];
const centerPart = [];
const rightPart = [];
arr.forEach((item) => {
if (item < medium) leftPart.push(item);
if (item > medium) rightPart.push(item);
if (item === medium) centerPart.push(item);
})
return sort(leftPart).concat(centerPart).concat(sort(rightPart));
} else {
return arr;
}
}
解决方案
这里有几个问题。
首先,您需要小心使用 jsperf 进行正确测试。浏览器会进行各种优化,如果你一遍又一遍地对同一个数组进行排序,很难知道你是否真的在测试你的排序算法。您可以考虑在循环的每个测试中创建一个新的随机数组。
其次,您的就地排序没有良好的分区方案。快速排序的美妙之处在于它将数组拆分成对数收缩的块。您在这里并没有真正进行快速排序。它更像是冒泡排序,因为您的end
变量只是从数组长度计数到零,并且每次调用都循环遍历0 - end
列表。这需要 O(n^2) 时间。此外,由于pivotIndex
总是定义为end - 1
,这是一个完全浪费的递归:
sort(pivotIndex + 1, end)
要使就地快速排序起作用,您需要一个分区方案来重置枢轴的索引,然后再使用start - pivot
and pivot+1 - end
。
一种常见的分区方案是 Hoare 分区,您可以从数组的相对两侧移动两个变量,当您在枢轴的任一侧找到值时进行交换。这并不复杂,但很容易搞砸索引。
分区通常被编写为一个单独的函数,但为了让它接近你的,我只是将它内联:
const myquicksort2 = (sortedArray) => {
const sort = (start, end) => {
if (end <= start) return;
let pivot = sortedArray[end];
let left = start, right = end
// Partion
while(left <= right){
while (sortedArray[left] < pivot){
left++
}
while (sortedArray[right] > pivot){
right--
}
if (left <= right) {
[sortedArray[left], sortedArray[right]] = [sortedArray[right], sortedArray[left]]
left++
right--
}
}
sort(start, right);
sort(right + 1, end);
};
sort(0, sortedArray.length-1);
return sortedArray;
};
console.log(myquicksort2([5, 7, 2, 1, 11, 10]))
当我在 Node 中使用时间测试运行它时,它比创建数组的函数快得多。在你的 jsperf 中,它不是。但是,如果我为每个循环创建一个新数组,则更快地表明在 jsperf 的工作方式中我们没有看到某些东西。这是jsperf 的编辑
在我的笔记本电脑上,这个节点在大约 20 毫秒内对 100,000 个随机整数进行排序,非就地函数大约需要 50 毫秒。
推荐阅读
- angular - Ionic 3 subscribe 多次触发
- batch-file - 是否可以批量生成随机生成的变量?
- jestjs - Test suite failed to run. Cannot find module 'react-dom' from 'reactBatchedUpdates.js'
- powerbi - 如何在 Power BI 中过滤具有大于指示器的不同文本计数?
- c# - 仅安装 Specflow.MSTest 包时,Specflow 3.0 错误地生成 NUnit 测试
- vue.js - 如何使用 vue js 在图表 js 中获取循环数据
- javascript - React 防止组件在点击事件上重新渲染
- angular - Spring Security 跨域读取阻塞 (CORB) - Angular App
- angular - 如何在数据循环的每次迭代中处理 amchart 折线图?
- webpack - 在 Next.js App 中使用 @import sass 函数时出错