快速排序不排序高范围的数字

时间:2015-09-03 14:40:10

标签: c++ algorithm vector quicksort

void quickSort(vector<long>& numberList,long low, long high){
    long pivot, indexLow, indexHigh, temp;

    if(low<high){
        pivot = low;
        indexLow = low;
        indexHigh = high;

        while(indexLow<indexHigh){
            while(numberList[indexLow] <= numberList[pivot]){
            indexLow++;
            }
            while(numberList[indexHigh] > numberList[pivot]){
            indexHigh--;
            }

            if(indexLow<indexHigh){
                temp = numberList[indexLow];
                numberList[indexLow] = numberList[indexHigh];
                numberList[indexHigh] = temp;
                }
        }

        temp = numberList[pivot];
        numberList[pivot] = numberList[indexHigh];
        numberList[indexHigh] = temp;

        quickSort(numberList, low, indexHigh-1);
        quickSort(numberList, indexHigh+1, high);
    }
}

此代码完美地将给定的数字列表排序为10000,但我尝试使用100000并终止程序,请您告诉我我做错了什么?

4 个答案:

答案 0 :(得分:3)

你的程序中肯定存在一个错误:http://ideone.com./W0Chni

我只是将10个元素分类为2-1-3-8-1-6-8-9-9-9,并且在崩溃前不久它的indexLow为11。

对不起我的错,它运作正常。

我回应说它可能是堆栈溢出。您可以通过采用不同的支点(无论如何应该)来缓解可能性,但最好的方法是避免堆栈溢出是使用堆栈数据结构而不是递归:

  1. 将您的初始{low,high}推入堆栈
  2. 将函数包裹在while循环中,直到堆栈为空。每次迭代都会从堆栈顶部{low,high}开始。
  3. 不是递归,而是将{low,high}对推到堆栈上。
  4. 像这样:http://ideone.com/gwWSjV

    innerText

    Voilà:不再进行递归,可以毫不费力地对1,000,000个元素进行排序。

    您可以通过仅将第二次递归推入堆栈然后立即循环返回以执行第一次而不使用push / pop来优化它,但这不是一个巨大的收获。

答案 1 :(得分:2)

快速排序采取良好的支点非常重要。您正在使用第一个元素(实际上是您的low):

pivot = low;

因此,您将获得深度为100000的递归,因此其Stack Overflow。

答案 2 :(得分:2)

虽然可能像其他人说的那样堆栈溢出,但我对此表示怀疑。你的代码有一些错误,可能导致它访问数组中的超出位置,这将(可能,虽然不能保证)提示使用Segmentation Fault提前终止(或者它可能在其他情况下正常工作,&#39 ;为什么UB太糟糕了。)

考虑一下:

while(numberList[indexLow] <= numberList[pivot]){
    indexLow++;
}
while(numberList[indexHigh] > numberList[pivot]){
    indexHigh--;
}

如果数组中的每个数字都已小于或等于numberList[pivot]怎么办? indexLow会愉快地增加high,这很可能是数组的大小。您需要检查外部循环条件仍然存在的两个循环。所以,请改为:

while (indexLow < indexHigh && numberList[indexLow] <= numberList[pivot]) {
    indexLow++;
}
while (indexHigh > indexLow && numberList[indexHigh] > numberList[pivot]) {
    indexHigh--;
}

这确保了内部循环不会使外部条件无效;没有这个,所有的赌注都是关于你的代码破坏/不工作的原因。

然后我们有了这个:

temp = numberList[pivot];
numberList[pivot] = numberList[indexHigh];
numberList[indexHigh] = temp;

现在,如果你像我说的那样修复循环,这可能会有问题。循环可能已停止,因为每个元素都小于或等于pivot(在这种情况下,执行此交换操作是安全的),但循环可能已停止,因为indexLowindexHigh发生了冲突,在这种情况下,我们不知道numberList[indexLow]是否实际上大于枢轴,或者它是否仍然小于或等于枢轴。所以我们需要手动测试它,并可能递减indexLow以找到交换枢轴的值:

assert(indexLow == indexHigh);
assert(indexLow > low);

if (numberList[indexLow] > numberList[pivot])
    indexLow--;

assert(numberList[indexLow] <= numberList[pivot]);

temp = numberList[pivot];
numberList[pivot] = numberList[indexLow];
numberList[indexLow] = temp;

quickSort(numberList, low, indexLow-1);
quickSort(numberList, indexLow+1, high);

以下是包含这些修补程序的完整版本:

void quickSort(vector<long> &numberList, long low, long high) {
    long pivot, indexLow, indexHigh, temp;
    if (low<high) {
        pivot = low;
        indexLow = low;
        indexHigh = high;

        while (indexLow < indexHigh) {
            while (indexLow < indexHigh && numberList[indexLow] <= numberList[pivot]) {
                indexLow++;
            }
            while (indexHigh > indexLow && numberList[indexHigh] > numberList[pivot]) {
                indexHigh--;
            }

            if (indexLow < indexHigh) {
                temp = numberList[indexLow];
                numberList[indexLow] = numberList[indexHigh];
                numberList[indexHigh] = temp;
            }
        }

        assert(indexLow == indexHigh);
        assert(indexLow > low);

        if (numberList[indexLow] > numberList[pivot])
            indexLow--;

        assert(numberList[indexLow] <= numberList[pivot]);

        temp = numberList[pivot];
        numberList[pivot] = numberList[indexLow];
        numberList[indexLow] = temp;

        quickSort(numberList, low, indexLow-1);
        quickSort(numberList, indexLow+1, high);
    }
}

请注意,此实现比平常更复杂。通过在阵列中向后和向前移动,你不会获得太多收益。传统的实现更容易编码,更易于阅读和理解:

void quicksort_simpler(vector<long> &numberList, long low, long high) {
    if (low >= high)
        return;

    long pivot = low;
    long last = pivot;
    long i;

    for (i = pivot+1; i <= high; i++) {
        if (numberList[i] <= numberList[pivot]) {
            last++;
            swap(numberList[last], numberList[i]);
        }
    }

    swap(numberList[last], numberList[pivot]);

    quicksort_simpler(numberList, low, last-1);
    quicksort_simpler(numberList, last+1, high);
}

请务必加入<algorithm>以获取swap()的声明。

答案 3 :(得分:0)

对于10 ^ 5个元素,有太多的递归例程调用会超出函数堆栈容量并导致堆栈溢出。与最快的快速排序情况一样(当所有元素都已经排序O(n^2)时)时,递归关系为T(n) = T(n - 1) + Θ(n),这肯定会导致堆栈溢出。实际上,10 ^ 5足够大,导致最佳/普通情况下的堆栈溢出(O(n logn))。如果您的容器太大,请使用迭代方法进行快速排序。