递归快速排序遇到 StackOverflowException

发布于 2024-09-02 02:41:59 字数 2181 浏览 1 评论 0原文

我正在 GenericList 类中实现递归快速排序方法。我将有第二种方法,它接受一个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;
    }

}

I am working on a Recursive QuickSort method implementation in a GenericList Class. I will have a second method that accepts a compareDelegate to compare different types, but for development purposes I'm sorting a GenericList<int>.

I am recieving stackoverflow areas in different places depending on the list size.

I've been staring at and tracing through this code for hours and probably just need a fresh pair of (more experienced) eyes. I definitely want to learn why it is broken, not just how to fix it.

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;
    }

}

Finished Product:

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;
    }

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏末 2024-09-09 02:41:59

您的实现是将枢轴包含在子列表中。通过将枢轴包含在子列表中(在本例中为左侧列表,因为条件为 <=),如果该枢轴最终位于子列表的中间,则您将自己设置为可能的无限递归。

示例:

  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 ) Left = [3, 6, 12, 4, 8] Right = [ Empty ]
  4. ... 无限循环

由于未排除枢轴(尽管其最终位置已知),因此可能会导致您永远对同一个列表进行排序,而不是减小列表的大小以在每次递归调用时进行排序。

如果您从子列表中排除数据透视表(按索引,而不是按值)并将其添加回 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]);
        ...

另请参阅:有关快速排序的维基百科文章(带有伪代码)

Your implementation is including the pivot in your sublists. By including the pivot in your sublists (in this case your left list because your condition is <=), you are setting yourself up for a possible infinite recursion if that pivot ends up in the middle of the sublist.

Example:

  1. List = [3, 6, 12, 4, 8] Pivot = 3 (12) Left = [3, 6, 12, 4, 8] Right = [ Empty ]
  2. List = [3, 6, 12, 4, 8] Pivot = 3 (12) Left = [3, 6, 12, 4, 8] Right = [ Empty ]
  3. List = [3, 6, 12, 4, 8] Pivot = 3 (12) Left = [3, 6, 12, 4, 8] Right = [ Empty ]
  4. ... Infinite Loop

Because the pivot is not excluded (although its final position is known), it can result in you sorting the same list over and over forever, rather than decreasing the size of the lists to sort with each recursive call.

If you exclude your pivot (by index, not by value) from the sublists and add it back it into the final tempList between leftList and rightList, it will work properly.

        ...
        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]);
        ...

See Also: Wikipedia article on Quicksort (with pseudocode)

疯狂的代价 2024-09-09 02:41:59

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

if(i != pivot)

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

编辑:

尼克是正确的,因为您会遇到其他情况(例如 {5,2})的问题。但是,即使您通过不将数据透视表放入将再次排序的列表中来解决当前问题,您也可能需要确保您的代码可以处理重复的元素。全部相同数字的大数组会给你带来 O(N^2) 时间复杂度,如果 N 足够大,那么你会遇到堆栈溢出。

I wouldn't recommend putting the pivot into one of the two groups. If you have just two elements that are equal, you can get an infinite loop. For instance, if you try to sort the array {1,1}, you should get an infinite loop which could be the cause of your stack overflow. Most quicksorts avoid this by swapping the pivot with an element on the edge and then sorting the rest of the array. Another way to handle this would be to put in a line to make sure that you don't add the pivot to the leftList like

if(i != pivot)

Then you'd add the pivot to the tempList in between adding the leftList and adding the rightList.

Edit:

Nick is correct in that you will get the problem for other cases like {5,2}. However, even if you fix the current problem by not putting the pivot into a list that will be sorted again, you might want to make sure that your code can handle repeated elements. An large array of all the same number will give you O(N^2) time complexity, and if N is big enough, then you will get a stack overflow.

神经暖 2024-09-09 02:41:59

看看当您有一个包含 [5,2] 的 List 时会发生什么。 您的 pivot 将等于 1,所以用于比较的值为5this[i].CompareTo(this[pivot]) <= 0 行将为 True,并且将放置数字 5leftList中。下一次与 2 的比较也将是 True,将 2 放在 leftList 中。现在,您的 leftList 将与它开始的位置完全相同:[5,2],您将再次调用 QuickSort...它会出现完全相同的令人作呕的结果:StackOverflow。

Look what happens when you have a List containing [5,2]. Your pivot will equal 1, so the value used to compare will be 5. The line this[i].CompareTo(this[pivot]) <= 0 will be True, and the number 5 will be placed in leftList. Your next comparison with 2 will also be True, placing the 2 in leftList. Now your leftList will be exactly what it started with: [5,2], which you will call QuickSort with all over again...and it will come out exactly the same ad nausem: StackOverflow.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文