了解快速排序期间的递归

Posted

技术标签:

【中文标题】了解快速排序期间的递归【英文标题】:Understanding recursion during QuickSort 【发布时间】:2016-04-25 12:29:06 【问题描述】:

我是 C++ 新手,正在经历来自 http://geeksquiz.com/quick-sort/ 的一种快速排序算法

这里是sn-p的代码,我无法理解low和high的值怎么变了

int partition (int arr[], int low, int high)

    int pivot = arr[high];    // pivot
    int i = (low - 1);  // Index of smaller element

    for (int j = low; j <= high- 1; j++)
    
        // If current element is smaller than or
        // equal to pivot
        if (arr[j] <= pivot)
        
            i++;    // increment index of smaller element
            swap(&arr[i], &arr[j]);
        
    
    swap(&arr[i + 1], &arr[high]);
    return (i + 1);


/* The main function that implements QuickSort
 arr[] --> Array to be sorted,
  low  --> Starting index,
  high  --> Ending index */
void quickSort(int arr[], int low, int high)

    if (low < high)
    
        /* pi is partitioning index, arr[p] is now
           at right place */
        int pi = partition(arr, low, high);

        // Separately sort elements before
        // partition and after partition
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    

请帮我理解上面的逻辑。

【问题讨论】:

理解这个逻辑的最好方法是拿出一张纸和一支铅笔,写下一个小数组,比如五个元素。然后,一次一行地浏览partition() 中的代码,跟踪每个变量和数组的内容。 lowhigh 只是递归排序的边界 @SamVarshavchik 我可以理解 partition() 背后的逻辑。我无法理解的一件事是递归逻辑和低值更新。由于函数中没有我们正在更新低/高的值。 【参考方案1】:

找到一个分区。 partition 的后置条件是枢轴元素左侧的所有内容都小于或等于分区元素,枢轴元素右侧的所有内容都大于或等于枢轴元素。递归左右子数组。

通过循环不变量,您将拥有一个排序数组。


为简单起见,假设您的分区始终返回中间元素。

您知道partition 的后置条件确保left 最多是pivot 元素,right 至少是pivot 元素。

您现在通过使用low == lowhigh == pi - 1 递归调用快速排序来递归左排序。 pi 位于正确的空间中,因此您无需担心。最后,您使用low == pi+1high == high 在正确的数组上调用快速排序。

重复直到所有内容都排序完毕(即!(low &lt; high))。

递归任务在这张图中得到了很好的解释(我们假设枢轴每次都是中间元素)。这也方便地显示了平均情况 O(n log n) 时间复杂度。

【讨论】:

谢谢埃里普...但我想了解低/高值在逻辑的哪一步得到更新。 @vivek 您在每个递归级别上分别用pi-1pi+1 更新highlow。这对应于左右子数组的递归。【参考方案2】:

您已澄清您的问题,即您了解 partition() 背后的逻辑,但不了解递归。好的。

您必须先假设quickSort() 将对您的数组进行排序。接受它作为一个给定的。一个公理。一定是真的。您确信quickSort() 将对您的数组进行排序。你必须接受这句话作为一个不容置疑的真理,作为一个起点。

那么,您已经了解partition() 将列表分为两半。基于此,您可以得出以下结论:

    在枢轴元素之前的一半数组只包含小于枢轴元素的值。

    在枢轴元素之后的数组的一半只包含大于枢轴元素的值。

    1 和 2 是 partition() 操作的结果,您说您完全理解。以 1 和 2 为起点,您可以得出结论,如果 1 和 2 中引用的数组的一半本身是完全排序的,那么整个数组就必须是完全排序的。

    你如何使 1 和 2 为真?好吧,你递归地应用quickSort() 算法。您刚刚同意quickSort() 将对它获得的数组进行完全排序。所以,quickSort()对两半递归排序后,最终的结果一定是一个完全排序的列表。

Q.E.D.

附:上面使用的术语“阵列的一半”是一个松散使用的术语。当然,每一半数组的实际大小不会正好是原始数组的一半。这对整体逻辑没有影响。

【讨论】:

以上是关于了解快速排序期间的递归的主要内容,如果未能解决你的问题,请参考以下文章

C语言,快速排序算法

归并排序和快速排序

数据结构排序

尾递归

数据结构和算法之排序二:快速排序

算法设计与分析分治法--快速排序的递归和非递归实现