【问题标题】:Stack overflow in quicksort快速排序中的堆栈溢出
【发布时间】:2014-02-10 23:34:38
【问题描述】:

在开始之前,我只想说我在过去 2 个小时左右的时间里阅读了关于“快速排序”和“堆栈溢出”的 SO 帖子,所以我不只是随机询问,我真的不知道怎么办……

好的,我必须实现一个快速排序算法,但由于某种原因我找不到,我不断收到堆栈溢出错误。我用的是VS2010,所以从头到尾调试过。

所以,这是我的代码:

const int THRESHOLD = 1;

std::vector<int> data = *new std::vector<int>();

void GetPivotPoint( bool first, int leftBound, int rightBound, int& pivotID )
{
    if( first ) // We are choosing the first element as a pivot
    {
        pivotID = 0;
        return;
    }
    else // We are choosing the median between 1, n/2 and n for our pivot
    {
        int left = leftBound;
        int right = rightBound;
        int center = ( left + right ) / 2;

        if( data[center] - data[left] < 0 ) data_manip::Swap( data, left, center );
        if( data[right] - data[left] < 0 ) data_manip::Swap( data, left, right );
        if( data[right] - data[center] < 0 ) data_manip::Swap( data, center, right );

        data_manip::Swap( data, center, right );
        pivotID = right;
    }
}

int Partition( int left, int right )
{
    int pivotID = 0;
    GetPivotPoint( true, left, right, pivotID );
    int pivot = data[pivotID];

    int i = left - 1;
    int j = right + 1;

    while( i < j )
    {
        i++;
        while( data[i] < pivot ) i++;
        j--;
        while( data[j] > pivot ) j--;

        if( i < j )
        {
            data_manip::Swap( data, i, j );
        }
    }

    return j;
}

void Quicksort( int left, int right )
{
    if( left + THRESHOLD > right )
    {
        algo::Bubblesort( data, left, right );
    }
    else
    {
        int partitionPoint = Partition( left, right );

        Quicksort( left, partitionPoint );
        Quicksort( partitionPoint + 1, right );
    }
} 

这是 Swap() 方法的实现

inline void Swap( std::vector<int>& data, int p1, int p2 )
    {
        int temp = data[p1];
        data[p1] = data[p2];
        data[p2] = temp;
    }

我正在使用超过 1k 到 500K 的集合。另外,在这个特定的代码中,我使用了将第一个元素作为枢轴的选项,现在我知道这不是最佳的,但我也需要测试该选项。如果我确实使用三的中位数而不是第一个元素,那么我不会得到堆栈溢出,但是,当我使用第一个元素作为枢轴时可能会导致它。

帮助的坦克

【问题讨论】:

  • “由于某种原因我找不到” - 这将是递归。堆栈的大小是有限的,每个递归调用都会吃掉它的一部分。 (使用三的中位数,您不太可能遇到快速排序的病态最坏情况,因此不太可能递归太深。)
  • 这是为了你自己的学习目的吗?否则,std::sort.
  • Knuth 和 Sedgewick 都提到最好先在较小的分区上递归,以节省堆栈增长。大多数实现使用低于阈值的插入排序。在这个或任何其他应用程序中,冒泡排序没有什么可推荐的。
  • 以下不是堆栈溢出的来源,而是错误代码和内存泄漏。当您创建 vector 时,只需执行 std::vector&lt;int&gt; data; 即可。您使用new 的方式是内存泄漏和非常奇怪的做法。

标签: c++ sorting quicksort


【解决方案1】:

由于以下行,您会出现堆栈溢出。这是微妙但非常重要的。您必须将较低的偏移量添加到您计算的中间值。

int center = ( left + right ) / 2;

试试这个:

int center = left + (right - left) / 2;

/////////////////////////////////////// ///////////////////////////////////////////////p>

另外,我有一个简单而优雅的实现。它被模板化,以便它可以与任何数据类型一起使用。该解决方案利用了 C++11 移动语义和良好的 API 设计。

如果数组大小可能少于 7 个项目,您可以通过执行插入排序或冒泡排序来优化它,这是一个好主意,因为快速排序本身对于小数组大小来说开销太大

#include <vector>
#include <memory>

template <typename Item>
bool less(Item v, Item w)
{
    // Below line is written assuming that you data implements compareTo method
    // You can also replace it with simple v < w
    return v.compareTo(w) < 0;
}

template <typename Item>
void exch(std::vector<std::shared_ptr<Item>> &arr, int i, int j)
{
    if (i == j) return;

    arr[i].swap(arr[j]);
}

template <typename Item>
class QuickSort
{
public:
    static void sort(std::vector<std::shared_ptr<Item>> &arr)
    {
        sort(arr, 0, arr.size() - 1);
    }

private:
    static void sort(std::vector<std::shared_ptr<Item>> &arr, int lo, int hi)
    {
        if (hi <= lo) return;
        int j = partition(arr, lo, hi);
        sort(arr, lo, j - 1);
        sort(arr, j + 1, hi);
    }

    static int partition(std::vector<std::shared_ptr<Item>> &arr, int lo, int hi)
    {
        int i = lo, j = hi + 1;

        while (true)
        {
            // Find item on left to swap
            while (less((*arr[++i]), (*arr[lo])))
                if (i == hi) break;

            // Find item on right to swap
            while (less((*arr[lo].get()), (*arr[--j].get())))
                if (j == lo) break;

            // Check if pointers swap
            if (i >= j) break;

            // Swap
            exch(arr, i, j);
        }

        // Swap with partitioning item
        exch(arr, lo, j);

        return j;
    }
};

【讨论】:

  • Nitpick,只有静态成员的类实际上只是一个命名空间。
  • 我同意。这可以通过将方法设为非静态或根本不使用类来改进。我只想公开一个 API 来对客户端的私有方法进行排序和隐藏。