我试图使快速排序算法通过选择第一项作为枢轴始终在分区函数中,并返回该枢轴项的索引以在快速排序函数中使用…我不知道为什么我面临这个错误
函数分区(项目,左,右){^RangeError:超过最大调用堆栈大小
整个代码如下
var items = [5, 3, 6, 7, 2, 9];
function swap(items, leftIndex, rightIndex) {
var temp = items[leftIndex];
items[leftIndex] = items[rightIndex];
items[rightIndex] = temp;
}
function partition(items, left, right) {
var pivot = items[0];
i = left;
j = right;
while (i < j) {
do {
i++;
} while (items[i] <= pivot);
do {
j--;
} while (items[j] > pivot);
if (i < j) {
swap(items, i, j);
}
}
swap(items, 0, j);
return j;
}
function quickSort(items, left, right) {
var index;
if (items.length > 1) {
index = partition(items, left, right); //index returned from partition
if (left < index - 1) {
//more elements on the left side of the pivot
quickSort(items, left, index - 1);
}
if (index < right) {
//more elements on the right side of the pivot
quickSort(items, index, right);
}
}
return items;
}
var sortedArray = quickSort(items, 0, items.length - 1);
//console.log(sortedArray); / / expected[(2, 3, 5, 6, 7, 9)];
如果分区(项目,左,右)
是为了获取整个数组(项目)及其左右两端的索引,那么我认为支点不应该是项目[0]
,而应该是项目[左]
。同样,在最后,它应该是交换(项目,左,j)
。
但是,请注意,如果分区
中的数组将其最小的元素作为其第一个元素,例如[5,7,6,9],则会发生什么。然后5将是支点,i
将落在7上,j
将落在5上,并且j=左
将返回,此时在快速排序
中您将拥有index=左
,然后您将执行快速排序(项目,索引,右)
,即快速排序(项目,左,右)
,从而导致无限递归。因此,您应该执行快速排序(项目,索引1,右)
,因为支点已经在它的正确位置上,不应该进一步移动。
这些更改后,代码如下所示:
var items = [5, 3, 6, 7, 2, 9];
function swap(items, leftIndex, rightIndex) {
var temp = items[leftIndex];
items[leftIndex] = items[rightIndex];
items[rightIndex] = temp;
}
function partition(items, left, right) {
var pivot = items[left];
console.log("partition: " + items + " " + left + " " + right);
i = left;
j = right;
while (i < j) {
do {
i++;
} while (items[i] <= pivot);
do {
j--;
} while (items[j] > pivot);
if (i < j) {
swap(items, i, j);
}
}
swap(items, left, j);
return j;
}
function quickSort(items, left, right) {
console.log("quickSort: " + items + " " + left + " " + right);
var index;
if (right - left > 1) {
index = partition(items, left, right); //index returned from partition
if (left < index - 1) {
//more elements on the left side of the pivot
quickSort(items, left, index - 1);
}
if (index + 1 < right) {
//more elements on the right side of the pivot
quickSort(items, index + 1, right);
}
}
return items;
}
var sortedArray = quickSort(items, 0, items.length - 1);
// returns [2,3,5,7,6,9]
这意味着分区
有错误,我把这个问题留给你。也许维基百科或G4G网站会帮助你编写正确的分区算法。(提示:这与i
和j
在分区
中相互传递有关,导致了最初的问题。
但这不是结束!
如果您每次都选择第一个元素作为支点,您可以得出相同的错误。想象一下,数组已经排序,所以假设输入是[1,2,3,4,5]。然后在第一次调用快速排序时,您将数组划分为1和[2,3,4,5]。然后,您将在右侧数组上递归调用该函数,将其划分为2和[3,4,5]等等。
这意味着,如果你的数组很大(比如,它有100万元素),那么你将有一个非常大的堆栈大小(至少有200万个函数调用,100万个分区
和100万个快速排序
)。这是由于不幸的枢轴选择。
要解决这个问题,您应该随机化支点。这样,您的堆栈大小大于对数的机会非常小(基本上为零),并且问题可以毫不费力地解决。