【问题标题】:Is This a QuickSort?这是快速排序吗?
【发布时间】:2025-12-31 19:20:07
【问题描述】:

我正在尝试为我自己的启迪编写一个快速排序。我使用pseudocode on wikipedia 作为指导。我的代码有效。它似乎应该在 O(n log n) 时间内运行。我尝试实际计时我的代码以检查复杂性(即,当我将输入大小加倍时,运行时间是否增加了大约 n log n),但我仍然不确定。

我的代码看起来很简单。我就地进行排序。我见过的其他实现使用了我没有的分区函数。这让我觉得我已经实现了一些其他的排序算法。这是快速排序算法吗?

public static void QuickSortInPlace(int[] arr)
{
    QuickSortInPlace(arr, 0, arr.Length - 1);
}

private static void QuickSortInPlace(int[] arr, int left, int right)
{
    if (left < right)
    {
        //move the middle int to the beginning and use it as the pivot
        Swap(arr, left, (left + right) / 2);

        int pivotIndex = left;
        int pivot = arr[pivotIndex];
        int min = left + 1;
        int max = right;

        for (int i = min; i <= max; i++)
        {
            if (arr[i] > pivot)
            {
                Swap(arr, i, max);
                max--; //no longer need to inspect ints that have been swapped to the end
                i--; //reset the loop counter to inspect the new int at the swapped position
            }
        }

        //move pivot to its sorted position
        Swap(arr, max, pivotIndex);

        //recurse on the sub-arrays to the left and right of the pivot
        QuickSortInPlace(arr, left, max - 1);
        QuickSortInPlace(arr, max + 1, right);
    }
}

基于 Dukeling 的回应的第二个版本。

public static void QuickSortInPlace3(int[] arr, int min, int max)
{
    if (min < max)
    {
        int pivotIndex = min;
        int pivot = arr[pivotIndex];

        int left = min;
        int right = max;

        while (left <= right)
        {
            while (arr[left] < pivot)
                left++;

            while (arr[right] > pivot)
                right--;

            if (left <= right)
            {
                Swap(arr, left, right);
                left++;
                right--;
            }
        }

        QuickSortInPlace3(arr, min, right);
        QuickSortInPlace3(arr, left, max);
    }
}

【问题讨论】:

    标签: c# algorithm quicksort in-place


    【解决方案1】:

    是的,它绝对足够接近快速排序来分类,或者至少是一个小变种。

    您正在对数据进行分区,只是没有单独的功能。

    你确实发生了一些奇怪的事情。

    通常,使用快速排序的就地变体,您在左侧索引小于枢轴时增加左侧索引,然后在右侧索引较大时减少右侧索引,然后交换两者(仅执行涉及 2 个元素的交换它们都在错误的一边)。

    然而,你只是增加了左边,所以你可以从右边交换已经更大的元素。尽管渐近运行时间应该相同,但这可能会导致不必要的交换次数。

    以下是“正常”变体的一些伪代码:(取自Rosetta Code

    while left ≤ right
        while array[left] < pivot
            left := left + 1
        while array[right] > pivot
            right := right - 1
        if left ≤ right
            swap array[left] with array[right]
            left := left + 1
            right := right - 1
    

    【讨论】:

    • 我无法理解您粘贴的伪代码。QuickSort 的循环不变式是在迭代之后,枢轴位于正确的位置。使用我在上面粘贴的第二个版本,我发现这不是真的(即使代码有效)。这是一个示例运行:输入 {3,2,4,1,5} -> 第一次交换 {1,2,4,3,5} -> 第二次交换 {1,2,3,4,5 }。我已经在每个步骤中突出显示了枢轴。第一次交换后,第一个枢轴3 不在其最终位置。这违反了循环不变量,但是在第二次交换之后,数组是有序的。你知道这是为什么吗?
    • 枢轴元素在哪里并不重要。如果它在左边,它将是该子数组中最大的元素并被放在最右边的位置。如果它在右边,它将是最小的并放在最左边的位置。因此,无论哪种方式,它最终都在中间。虽然你自己把它放在中间会导致比较少,因为你再也不会把它和任何东西比较了。