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

Quick Sort not sorting numbers of high range

本文关键字:范围 数字 排序 高范围 快速排序      更新时间:2023-10-16
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,程序被终止,你能告诉我我做错了什么吗?

你的程序肯定有bug: http://ideone.com./W0Chni

我只对10个元素进行排序2-1-3-8-1-6-8-9-9,它在崩溃前不久以indexLow为11结束。

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

我提示可能是堆栈溢出。您可以通过采用不同的枢轴来减轻这种可能性(无论如何都应该这样做),但是避免堆栈溢出的最佳方法是使用堆栈数据结构而不是递归:

  1. 将初始{low,high}推送到堆栈
  2. 在while循环中包装函数,直到堆栈为空。每次迭代都从堆栈中取出顶部{low,high}。
  3. 不是递归,而是将{low,high}对压入堆栈。

像这样:http://ideone.com/gwWSjV

void quickSort(std::vector<long>& numberList, long low, long high)
{
    struct lh { long low; long high; };
    std::vector<lh> stack;
    stack.push_back(lh{low, high});
    while (!stack.empty())
    {
        lh popped = stack.back();
        stack.pop_back();
        low = popped.low;
        high = popped.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);
            stack.push_back(lh{low, indexHigh-1});
            //quickSort(numberList, indexHigh+1, high);
            stack.push_back(lh{indexHigh+1, high});
        }
    }
}

volil:不再递归,对1,000,000个元素进行排序,一点也不麻烦。

你可以优化它,只将第二次递归压入堆栈,然后立即循环执行第一次,而不需要push/pop,但这不是一个巨大的收益。

虽然可能像其他人说的那样是堆栈溢出,但我对此表示怀疑。你的代码有一些错误,这可能会导致它访问超出数组的位置,这将(可能,虽然不能保证)提示提前终止与分割故障(或者它似乎在其他情况下工作良好,这就是为什么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;

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

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()的声明

对于快速排序来说,选择一个好的枢轴非常重要。您正在获取第一个元素(实际上是您的low):

pivot = low;

因此你得到了深度100000的递归,所以它的堆栈溢出

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