【问题标题】:Skiena's Quick Sort implementationSkiena 的快速排序实现
【发布时间】:2016-01-10 06:19:01
【问题描述】:

我很难理解 Skiena 的快速排序。具体来说,他在用partition函数,尤其是firsthigh参数做什么?

quicksort(item_type s[], int l, int h) {
    int p;                  /* index of partition */
    if ((h - l) > 0) {
            p = partition(s, l, h);
            quicksort(s, l, p-1);
            quicksort(s, p+1, h);
    }
}

我们可以通过维护数组的三个部分将数组划分为特定枢轴元素的一次线性扫描:小于枢轴(firsthigh 左侧),大于或等于枢轴(@987654325 之间) @ 和 i),未探索(在 i 的右侧),实现如下:

int partition(item_type s[], int l, int h) {

    int i;           /* counter */
    int p;           /* pivot element index */
    int firsthigh;   /* divider position for pivot element */

    p = h;
    firsthigh = l;
    for (i = l; i  <h; i++) {
        if (s[i] < s[p]) {
            swap(&s[i],&s[firsthigh]);
            firsthigh ++;
        }

    swap(&s[p],&s[firsthigh]);
    return(firsthigh);
}

【问题讨论】:

  • firsthigh 是不小于s[p] 的第一个(即最左、最低的索引)元素的索引。你试过用铅笔和纸跑partition(...)吗?

标签: c algorithm sorting quicksort


【解决方案1】:

在任何时候,严格在firstHigh 左侧的所有内容都小于枢轴(请注意,此集合中最初没有元素),并且其右侧或右侧的所有内容都是未知的,或已知为 >= 枢轴。将firstHigh 视为下一个我们可以放置低于基准值的位置。

此算法与原地算法非常相似,您可以使用该算法删除 >= 枢轴的所有项目,同时将剩余项目“压缩”到尽可能左侧。对于后者,您将维护两个索引 lfirstHigh(您可以分别认为是 fromto),它们都从 0 开始,并在数组中遍历 l;每当您遇到不应删除的s[l] 时,将其分流到尽可能左的位置:即,将其复制到s[firstHigh],然后增加firstHigh。这是安全的,因为我们总是有firstHigh &lt;= l。这里唯一的区别是我们无法覆盖当前位于s[firstHigh] 的已删除(可能->=-to-pivot)项目,因此我们交换了这两个项目。

【讨论】:

    【解决方案2】:

    我建议在阅读此答案及其考虑的示例案例时,用铅笔和纸进行推理

    sn-p 中缺少一些括号:

    int partition(item_type s[], int l, int h)
    {
      int i;/* counter */
      int p;/* pivot element index */
      int firsthigh;/* divider position for pivot element */
      p = h;
      firsthigh = l;
      for (i = l; i < h; i++) {
    
        if (s[i] < s[p]) {
          swap(s[i], s[firsthigh]);
          firsthigh++;
        }
      }
    
      swap(s[p], s[firsthigh]);
      return(firsthigh);
    }
    
    void quicksort(item_type s[], int l, int h)
    {
      int p;                  /* index of partition */
      if ((h - l)>0) {
        p = partition(s, l, h);
        quicksort(s, l, p - 1);
        quicksort(s, p + 1, h);
      }
    }
    

    无论如何,分区函数的工作原理如下:假设我们有大小为 5 的数组 { 2,4,5,1,3 }。该算法将最后一个元素 3 作为枢轴并开始迭代地探索项目:

    2 第一次遇到.. 因为2 小于枢轴元素3,它与firsthigh 指向的位置0 交换。这没有效果,因为2 已经在位置 0

    2,4,5,1,3
    ^
    

    firsthigh 递增,因为2 现在是该位置的稳定值。

    然后遇到4。这次4 大于3 (大于枢轴),因此不需要交换。请注意firsthigh 继续指向45 也是如此。

    遇到1时,该值应放在2之后,因此与firsthigh指向的位置交换,即与4的位置交换

    2,4,5,1,3
      ^   ^ swap
    2,1,5,4,3
        ^ now firsthigh points here
    

    当元素结束时,枢轴元素与firsthigh的位置交换,因此我们得到

    2,1,| 3,| 4,5
    

    请注意如何将小于枢轴的值放在左侧,而大于枢轴的值保留在右侧。正是分区函数所期望的。

    返回枢轴元素的位置,并在枢轴左右的子数组上重复该过程,直到遇到一组0元素(if条件是递归的底部)。

    因此firsthigh 的意思是:我知道的大于枢轴的第一个元素。在上面的示例中,firsthigh 放在第一个元素上,因为我们仍然不知道该元素是大于还是小于枢轴

    2,4,5,1,3
    ^
    

    一旦我们意识到2 不是第一个大于枢轴的元素,或者我们在那个位置交换了一个小于枢轴的元素,我们就会尝试保持我们的不变量有效:好的,先提高 firsthigh 并将 4 视为大于枢轴的第一个元素。这为我们提供了教科书中引用的三个部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-29
      • 2017-02-18
      相关资源
      最近更新 更多