【问题标题】:How does recursion of Quick sort work?快速排序的递归如何工作?
【发布时间】:2016-03-19 01:56:21
【问题描述】:

下面的函数是快速排序的一个实现。在这里,我们将最后一个元素作为枢轴。

我理解partition 函数(枢轴到达其排序位置的位置),但我无法理解递归函数qs。函数qs 递归调用自身,以qs(a,start,pi-1) 求解左侧,qs(a,pi+1,end) 求解右侧。

它是先求解左边,然后是(左边),然后是(左边),等等,然后是左,左...右,等等。还是交替先解决左边,再解决右边。

PS:我想知道计算机内部发生了什么,这种快速排序的递归机制。该程序正在运行,但我想知道它是如何工作的。

int partition(int *a, int start, int end)
{   
    int pivot=a[end];
    int pi=start;
    for(int i=start; i<end; i++)
    {
        if(a[i]<=pivot)
        {
            swap(a[i],a[pi]);
            pi++;
        }
    }
    swap(a[pi], a[end]);
    return pi;
}

void qs(int*a, int start, int end)
{
    if(start<end)
    {
        int pi=partition(a,start,end);
        qs(a,start,pi-1);
        qs(a,pi+1,end);
    }
}

【问题讨论】:

  • 第一个(左,左,左,左,...右)。对qs 的第一次递归调用位于数组的左分区。在第一次调用返回之前,不会对数组的右分区进行递归调用,即直到左分区完全排序。另一种看待这个问题的方式是,您正在执行递归树的深度优先遍历。
  • @beaker 在第一个分区之后,左边又再次向左分区,但是左侧分区的右侧(左侧的第二个分区,它的右侧)呢?
  • @beaker 另外,左侧(第一个分区)排序后,在对右侧qs(a,pi+1,end)进行排序时,左侧函数qs不会(a,start,pi-1) 也被调用?那么右侧(第一个分区)不会按与左侧不同的进程排序吗?
  • 你应该阅读子程序,即函数调用和结果call stack structure

标签: algorithm sorting recursion quicksort divide-and-conquer


【解决方案1】:

Lomuto 分区方案的操作顺序示例,其中 pivot = array[high]。

快速排序(数组,低,pivot-1),快速排序(数组,pivot+1,高)。

用于显示左子数组、枢轴、右子数组的竖线。

  11 13 14 12 10  8  9  5  6  4  2  0  1  3  7
   5  6  4  2  0  1  3 11 13 14 12 10  8  9  7 
   5  6  4  2  0  1  3| 7|13 14 12 10  8  9 11
   2  0  1  5  6  4  3 
   2  0  1| 3| 6  4  5
   0  2  1 
   0| 1| 2
               4  6  5
               4| 5| 6
                          10  8  9 13 14 12 11
                          10  8  9|11|14 12 13
                           8 10  9
                           8| 9|10
                                      12 14 13
                                      12|13|14
   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14

【讨论】:

  • 太棒了!正如旁注:在您的示例中,分区(左侧和右侧)始终具有相同的大小。事实并非如此。比如你有8 4 7 6 5 9 2 1 3,那么pivot就是3,左边分区有2个元素,右边6个元素
  • @SQLOTL - 我试图做一个“漂亮”的例子,主要是为了展示深度优先/左优先的概念。
  • 是的,这是最佳案例。然后你有 O (n*log(n))。但实际情况有所不同;)顺便说一句,最坏情况是原始列表是反向排序的。然后快速排序变为 O(n^2)。事实上,现实介于最好和最坏的情况之间。
  • @SQLOTL - 如果Hoare partition scheme,使用 pivot = array[(low+high)/2],那么它对于排序或反向排序的数据是最快的。仍然存在最坏情况的数据模式,但至少处理了简单的情况。
【解决方案2】:

我可以建议您了解事情发生顺序的最佳方法是在您的 qs 方法中打印一些调试信息。为此,我将通过 ref 添加一个附加参数,其中我将计算调用 qs 函数的次数,并将该信息打印在正在解决的分区边界旁边。例如

void qs(int*a, int start, int end, int &stepCount)
{
    if(start<end)
    {
        int currentStep = stepCount++;
        cout << "Solving step " << currentStep << " partition from " << start << " to " << end << endl;
        int pi=partition(a,start,end);
        qs(a,start,pi-1,stepCount);
        qs(a,pi+1,end,stepCount);
        cout << "Finished solving step " << currentStep << endl;
    }
}

不明白你的 PS 问题。它非常广泛。你的意思是具体在分区?递归是如何处理的?这些位是如何在内存中移动的?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多