【发布时间】:2020-04-01 20:38:41
【问题描述】:
我正在使用 QuickSort,我在 LeetCode 上为this problem 测试了这两种算法。两种算法都有效,但一种似乎比另一种快得多,我不明白为什么。
这个超出了 LeetCode 的时限:
function quickSort(array) {
quickSortHelper(array, 0, array.length - 1);
return array;
}
function quickSortHelper(array, start, end) {
// Edge case
if(start >= end) return;
// Always choose the pivot index at the beginning of the array.
const pivot = start;
// The left index is next to the pivot on the right.
let left = start + 1;
let right = end;
while (right >= left) {
if (array[left] > array[right] && array[right] < array[pivot])
swap(left, right, array);
if (array[left] < array[pivot]) left += 1;
if (array[right] > array[pivot]) right -= 1;
}
swap(pivot, right, array);
// Always sort the smaller sub-array first
const leftIsSmaller = right - 1 - start < end - (right + 1);
if (leftIsSmaller) {
quickSort(array, start, right - 1);
quickSort(array, right + 1, end);
}
else {
quickSort(array, right + 1, end);
quickSort(array, start, right - 1);
}
}
function swap(a, b, array) {
[array[a], array[b]] = [array[b], array[a]];
return array;
}
大O:
- 最差:时间 = O(n^2),因为如果主元不断被交换到未排序部分的末尾,那么我们每次都有 O(n - 1) ~ O(n) = O(n*n )
- 最佳:当枢轴将数组分成两半时,时间 = O(nlog(n))。
- 平均:时间 = O(nlog(n))
- Space = O(log(n)),因为递归使用 2 个子数组并首先针对较小的子数组进行快速排序。
这个没有超过 LeetCode 的时间限制,并且比大多数提交都快得多。大 while 循环中的 2 个 while 循环(对我而言)似乎更慢,但实际上更快。
function quickSort(array) {
sortHelper(array, 0, array.length - 1);
return array;
}
function sortHelper(array, start, end) {
if (start >= end) return;
let i = start;
let j = end;
let base = array[i];
while (i < j) {
while (i < j && array[j] >= base) j -= 1; // This makes sense but why this while loop has to happen before the other while loop?
array[i] = array[j]; // this line
while (i < j && array[i] <= base) i += 1;
array[j] = array[i]; // and this line. don't they make you lose access to the values in the array?
}
array[i] = base;
sortHelper(array, start, i - 1);
sortHelper(array, i + 1, end);
}
这是为什么呢?
【问题讨论】:
-
swap在循环中创建 2 个堆对象,而代码 B 只是增加/减少数字。这可能会受到伤害,但我一目了然地看不到任何时间复杂度差异。你做过分析吗? -
如果您在第一个中重写
swap()以便它与临时局部变量进行简单交换,则性能可能会更接近。 (无论如何,运行时可能会有效地执行解构分配;我不确定,但我肯定会尝试它。) -
@ggorlen 谢谢你的建议。我还没有完成分析,但我正在研究它。
-
@Pointy:我已经用
let tmp = array[a]; array[a] = array[b]; array[b] = tmp;重写了swap(),但是算法仍然超过了时间限制。 -
虽然时间复杂度保持不变,但您可以通过将内部 while 循环减少为单个条件来进一步加快快速排序的恒定时间部分,如 wiki pseudo code 所示。
标签: javascript arrays algorithm performance quicksort