QuickSort堆栈溢出

时间:2012-04-28 11:07:58

标签: c# stack-overflow quicksort

我的通用Qu​​ickSort算法有两个问题我无法确定原因。

  1. 该列表有时未正确排序
  2. 当处理大于20,000个项目的列表时,如果值重复很多(我认为不应该是真正的问题),我经常会遇到堆栈溢出。
  3. 列表未正确排序的情况相当罕见,因此列表必须相当大。 下面的粘贴显示了已安装软件的172个元素列表的前13个元素,第一列显示第一个排序后的输出,第二个列显示第二个排序。

    Adobe AIR                         7-Zip 4.65 (x64 edition)
    7-Zip 4.65 (x64 edition)          Adobe AIR
    Adobe Community Help              Adobe Community Help
    Adobe Encore CS4 Codecs           Adobe Encore CS4 Codecs
    Adobe Media Encoder CS4 Exporter  Adobe Media Encoder CS4 Exporter
    Adobe Media Encoder CS4 Importer  Adobe Media Encoder CS4 Importer
    Adobe Media Player                Adobe Media Player
    Adobe Reader X (10.1.0)           Adobe Reader X (10.1.0)
    Adobe Setup                       Adobe Setup
    Adobe Setup                       Adobe Setup
    Apple Application Support         Adobe Setup
    Adobe Setup                       Apple Application Support
    Apple Mobile Device Support       Apple Mobile Device Support
    ...                               ...
    

    正如您所看到的,第一次排序时,会出现一些不正确的行为,这可以通过另一种排序来解决。

    如果我对我的大型Windows事件日志列表进行排序,则会发生堆栈溢出的第二个问题。如果我在4周内排序52,000个日期,那似乎很高兴;但是,如果我排序52,000个具有多次重复的ID号,我的堆栈大小在崩溃系统之前就会变成998个项目。如果我按照主要是'gupdate'的列排序30,000个长列表,也会发生这种情况。

    现在,据我所知,堆栈计数应该是log2(n),因为添加到堆栈的数量等于完成的切割量。

    我让我的支点随机来帮助这个效果,但它没有太大的区别。

    Log2(60000)= 16.这对于堆栈溢出来说还不够!

    这是关注的代码:

    private static void QuickSortFunction<T>(T[] array, int start, int end, Comparer<T> comparer)
    {
        if (end - start >= 1)
        {
            int leftPtr, rightPtr, pivot;
            Random random = new Random();
            pivot = random.Next(start, end);
            Swap(array, pivot, start);
            pivot = start;
    
            leftPtr = start + 1;
            rightPtr = end;
    
            while (leftPtr < rightPtr)
            {
                while ((comparer.Compare(array[leftPtr], array[pivot])) <= 0 && (leftPtr < rightPtr))
                {
                    leftPtr++;
                }
    
                while ((comparer.Compare(array[rightPtr], array[pivot])) >= 0 && (leftPtr <= rightPtr))
                {
                    rightPtr--;
                }
    
                if (leftPtr < rightPtr)
                {
                    Swap(array, leftPtr, rightPtr);
                }
            }
            Swap(array, pivot, rightPtr);
            pivot = rightPtr;
    
            QuickSortFunction(array, start, pivot - 1, comparer);
            QuickSortFunction(array, pivot + 1, end, comparer);
        }
    }
    
    private static void Swap<T>(T[] array, int pointer1, int pointer2)
    {
        T temp = array[pointer1];
        array[pointer1] = array[pointer2];
        array[pointer2] = temp;
    }
    

    对于任何有兴趣的人,这是无序故障的修复。基本上,当它出现故障时,它无法识别2元素阵列。例如{E,B},它不会改变,因为它不会看自己的支点。

        if (end - start >= 1)
        {
            int leftPtr, rightPtr, pivot;
            Random random = new Random();
            pivot = random.Next(start, end);
            Swap(array, pivot, start);
            pivot = start;
    
            leftPtr = start;
            rightPtr = end;
    
            while (leftPtr < rightPtr)
            {
                while ((comparer.Compare(array[leftPtr], array[pivot])) < 0 && (leftPtr < rightPtr))
                {
                    leftPtr++;
                }
    
                while ((comparer.Compare(array[rightPtr], array[pivot])) >= 0 && (leftPtr < rightPtr))
                {
                    rightPtr--;
                }
    
                if (leftPtr < rightPtr)
                {
                    Swap(array, leftPtr, rightPtr);
                }
            }
            Swap(array, pivot, rightPtr);
            pivot = rightPtr;
    
            QuickSortFunction(array, start, pivot - 1, comparer);
            QuickSortFunction(array, pivot + 1, end, comparer);
        }
    

    一旦我为堆栈溢出编写解决方案,就会更新。

3 个答案:

答案 0 :(得分:1)

  

现在,据我所知,堆栈计数应该是log2(n),因为添加到堆栈的数量等于完成的切割量。

只有将输入分成类似 1 大小的两半时才会出现这种情况。例如,如果您对所有项目相等的列表进行排序,则会得到一个非常不均匀的分割,其中一侧没有元素,另一侧除了枢轴之外的所有内容。在这种情况下,您将获得 O(n)堆栈大小,因为每个级别仅将输入大小减小1.

避免这种情况的一种方法是使用三向分区,将所有元素放在中间等于枢轴。

1 如果分割总是好于某个常数比例,那你就没事了。

答案 1 :(得分:1)

让我们先看一下乱序问题。大循环一直持续到leftPtr >= rightPtr。由于你的第二个循环测试leftPtr <= rightPtr,有可能在最后leftPtr > rightPtr,在这种情况下你交换(在大循环之后)枢轴和一个被认为“OK”的元素( rightPtr指向leftPtr

传递的内容

不确定堆栈溢出问题,但是Hammar的建议似乎是合理的,特别是你说这个问题出现在很多相同元素的大型列表上

答案 2 :(得分:0)

观察您创建的无限循环...函数必须终止其内在值才能在使用中实现。