对快速排序感到困惑

时间:2018-01-08 10:11:56

标签: c# algorithm quicksort

目前我正在快速排序。我遵循了快速排序规则;但我发现了一件奇怪的事。

这个过程就像这张照片。

请帮我找到我的错误:

enter image description here

以下是代码:

  static void QuickSortFromMiddle(int[] arr, int low, int high)
    {

        if (low < high)
        {
            int middleValue = arr[(low+high)/2];
            int h = high+1;
            int l = low-1;
            while (l < h)
            {
                while (arr[--h] > middleValue && l<h);

                while (arr[++l] < middleValue && l<h) ;

                if (l >= h)
                    break; 
                int temp = arr[l];
                arr[l] = arr[h];
                arr[h] = temp;

            }

            QuickSortFromMiddle(arr,low,l-1);
            QuickSortFromMiddle(arr, h+1, high);
        }

    }
    /// <summary>
    /// 
    /// </summary>
    static void QuickSort(int[] arr)
    {
        QuickSortFromMiddle(arr, 0, arr.Length - 1);
    }

    /// <summary>
    /// 
    /// </summary>
    static void TestQuickSort()
    {

        var arr = new[] { 1, 5, 3, 4, 57, 5, 5, 53 };
        QuickSort(arr);
        foreach (int i in arr)
        {
            Console.WriteLine(i);
        }
    }

这是结果(我很困惑......)

enter image description here

正如Dukeling所说&#34;枢轴通常被移到任何一端&#34;

首先,我应该将数据枢轴放在数组的末尾

其次,我应该把枢轴放在arr的正确位置(大于左边,小于右边)

这是正确的过程:

enter image description here

2 个答案:

答案 0 :(得分:1)

整体算法如下:

  1. 从数组中选择一个名为pivot的元素。
  2. 分区:对数组进行重新排序,使得值小于枢轴的所有元素都在枢轴之前,而所有值大于枢轴的元素都在它之后(相等的值可以是任意一种)。在此分区之后,枢轴处于其最终位置。这称为分区操作。
  3. 递归地将上述步骤应用于具有较小值的元素的子数组,并分别应用于具有较大值的元素的子数组。
  4. 只要条件满足,有几种分区方案,其中任何一种都可以工作。您的分区方案是我以前从未见过的。特别是,我从未见过快速排序分区方案,它将集中定位的值作为支点。 有关一些标准分区方案(例如Lomuto),请参阅the wikipedia page

    总的来说,您的分区方案存在以下限制:

    1. 假设中心元素(就位置而言)是中位数。
    2. 不允许任意位置小于或大于中位数的元素。例如,在交换arr[l]arr[h]之前,您甚至无法检查是否需要交换它们。您只是假设在lh(两个内部while循环)的初始移动之后,需要交换所有其他数字。
    3. 您需要使分区方案更通用,或者尝试理解并使用其中一个标准方案。

答案 1 :(得分:1)

正如Parakram写的那样,枢纽元素是你的主要问题。 该算法不会将数组拆分到pivototelement的中间或位置,它会将数组拆分为两个,其中pivot- 。它将被分割的位置由l和h搜索。当他们见面时,你就有了分裂的位置。

我在您的代码中添加了一些注释。我觉得这个有用......

    static void QuickSortFromMiddle(int[] arr, int low, int high)
    {
        Console.WriteLine("Low: {0}, High: {1}, Arr: {2}", low, high, string.Join("|", arr));
        if (low < high)
        {
            int pivot = arr[high]; // Select you pivot element. After the run all smaller numbers will be left of it, all bigger ones on the high-side.
            int h = high;
            int l = low;

            // breaks at specific condition within the loop
            while(true)
            {
                // Search for the first element which is smaller, beginning on the high-side
                while (arr[h] >= pivot && l < h)
                {
                    h--;
                }

                // Search for the first element which is bigger, beginning on the low-side
                while (arr[l] < pivot && l < h)
                {
                    l++;
                }

                // we now have pivot (still at position "high")
                // we got an element which is bigger that pivot on "l"
                // we got an element which is smaller than pivot on "h"
                // conclusion: we need to change their positions

                Console.WriteLine("h: " + h + ", l: " + l +  ", Arr: " + string.Join("|", arr));

                // if l&h are at the same position, we're done and have to check if the pivot element has to be moved to this position
                if (l >= h)
                    break;

                // we change elements on position l and h, because we know that arr[l] is bigger that our pivot and arr[h] is smaller.
                int temp = arr[l];
                arr[l] = arr[h];
                arr[h] = temp;

            }

            // l equals h. This is now the position for pivot, because all elements on the lower side are smaller and all elements on the right side are bigger
            Console.WriteLine(">h: " + h + ", l: " + l + ", Arr: " + string.Join("|", arr));
            if (arr[l] > pivot)
            {
                arr[high] = arr[l];
                arr[l] = pivot;
            }

            // We start two new runs. One for the lower values: from Low to l and from l+1 to high.
            // Why? As we know l is our pivot value which splits the elements into two groups. smaller ones on the lower side, bigger ones on the higher side.
            // We now can focus on those two groups separately.
            QuickSortFromMiddle(arr, low, l);
            QuickSortFromMiddle(arr, l + 1, high);
        }

    }