递归QuickSort遭受StackOverflowException

时间:2010-05-18 03:24:45

标签: c# recursion stack-overflow quicksort

我正在使用GenericList类中的Recursive QuickSort方法实现。我将有第二个接受compareDelegate来比较不同类型的方法,但出于开发目的,我正在对GenericList< int>进行排序。

我正在接收不同地方的stackoverflow区域,具体取决于列表大小。

我一直在盯着并追踪这段代码几个小时,可能只需要一双新的(更有经验的)眼睛。我当然想知道为什么它被破坏,而不仅仅是如何解决它。

public void QuickSort()
{
    int i, j, lowPos, highPos, pivot;
    GenericList<T> leftList = new GenericList<T>();
    GenericList<T> rightList = new GenericList<T>();
    GenericList<T> tempList = new GenericList<T>();

    lowPos = 1; highPos = this.Count;
    if (lowPos < highPos)
    {
        pivot = lowPos;
        for (i = 2; i <= highPos; i++)
        {
            if (this[i].CompareTo(this[pivot]) <= 0)
                leftList.Add(this[i]);
            else
                rightList.Add(this[i]);
        }
        leftList.Add(this[pivot]);
        leftList.QuickSort();
        rightList.QuickSort();

        for(i=1;i<=leftList.Count;i++)
            tempList.Add(leftList[i]);
        for(i=1;i<=rightList.Count;i++)
            tempList.Add(rightList[i]);

        this.items = tempList.items;
        this.count = tempList.count;
    }

}

成品:

public void QuickSort()
{
    Random random = new Random();
    int i, j, lowPos, highPos, pivot;
    GenericList<T> leftList = new GenericList<T>();
    GenericList<T> rightList = new GenericList<T>();
    GenericList<T> tempList = new GenericList<T>();

    if (this.Count > 1)
    {
        pivot = random.Next(1,this.Count);
        for (i = 1; i <= this.Count; i++)
        {
            if (i == pivot) continue;
            if (this[i].CompareTo(this[pivot]) < 0)
                leftList.Add(this[i]);
            else
                rightList.Add(this[i]);
        }
        leftList.QuickSort();
        rightList.QuickSort();

        for(i=1;i<=leftList.Count;i++)
            tempList.Add(leftList[i]);
        tempList.Add(this[pivot]);
        for(i=1;i<=rightList.Count;i++)
            tempList.Add(rightList[i]);

        this.items = tempList.items;
        this.count = tempList.count;
    }

}

4 个答案:

答案 0 :(得分:2)

答案 1 :(得分:2)

我不建议将枢轴放入两组中的一组。如果你只有两个相等的元素,你可以得到一个无限循环。例如,如果您尝试对数组{1,1}进行排序,则应该得到一个无限循环,这可能是堆栈溢出的原因。大多数快速排序通过使用边缘上的元素交换枢轴然后对阵列的其余部分进行排序来避免这种情况。另一种处理方法是放入一行以确保不将枢轴添加到leftList,如

if(i != pivot)

然后在添加leftList和添加rightList之间添加枢轴到tempList。

编辑:

尼克是正确的,因为你会遇到像{5,2}这样的其他案件的问题。但是,即使您通过不将数据透视表放入将再次排序的列表来修复当前问题,您可能希望确保代码可以处理重复的元素。所有相同数字的大数组将为您提供O(N ^ 2)时间复杂度,如果N足够大,那么您将获得堆栈溢出。

答案 2 :(得分:2)

您的实施包括您的子列表中的数据透视表。通过在您的子列表中包含数据透视表(在这种情况下,您的左侧列表,因为您的条件是&lt; =),如果该数据透视点最终位于子列表的中间,您可以设置自己的无限递归。

示例:

  1. 列表= [3,6, 12 ,4,8]数据透视= 3( 12 )左= [3,6, 12 ,4,8]右= [空]
  2. 列表= [3,6, 12 ,4,8]数据透视= 3( 12 )左= [3,6, 12 ,4,8]右= [空]
  3. 列表= [3,6, 12 ,4,8]数据透视= 3( 12 )左= [3,6, 12 ,4,8]右= [空]
  4. ......无限循环
  5. 因为未排除数据透视(虽然其最终位置已知),但它可能导致您一遍又一遍地对相同列表进行排序,而不是减少列表的大小以便对每次递归调用进行排序。

    如果从子列表中排除您的数据透视(按索引而不是按值),并将其添加回leftList和rightList之间的最终tempList,它将正常工作。

            ...
            for (i = 1; i <= highPos; i++)
            {
                if (i == pivot) continue; // Add this
                if (this[i].CompareTo(this[pivot]) <= 0)
            ...
            for (i = 1; i <= leftList.Count; i++)
                tempList.Add(leftList[i]);
            tempList.Add(this[pivot]); // Add this
            for (i = 1; i <= rightList.Count; i++)
                tempList.Add(rightList[i]);
            ...
    

    另请参阅:Wikipedia article on Quicksort(带伪代码)

答案 3 :(得分:0)

如果您的List包含[5,2]. pivot将等于1,那么会发生什么情况,因此用于比较的值将为5。行this[i].CompareTo(this[pivot]) <= 0将为True,而5号码将放置在leftList中。您与2的下一次比较也将是True,将2置于leftList。现在,您的leftList将与它开始的完全相同:[5,2],您将再次调用QuickSort ...它将会出现完全相同的广告:StackOverflow。

相关问题