【问题标题】:Modifying this Quicksort to always use the last element as the pivot修改此快速排序以始终使用最后一个元素作为枢轴
【发布时间】:2011-01-19 23:31:03
【问题描述】:

我有以下Quicksort 始终选择子序列的第一个元素作为其枢轴:

void qqsort(int array[], int start, int end) {
    int i = start; // index of left-to-right scan
    int k = end; // index of right-to-left scan

    if (end - start >= 1) { // check that there are at least two elements to sort 
        int pivot = array[start]; // set the pivot as the first element in the partition
        while (k > i) { // while the scan indices from left and right have not met, 
            while (array[i] <= pivot && i <= end && k > i)  // from the left, look for the first element greater than the pivot
                i++;
            while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first element not greater than the pivot
                k--;
            if (k > i) // if the left seekindex is still smaller than the right index, swap the corresponding elements
                swap(array, i, k);              
        }
        swap(array, start, k); // after the indices have crossed, swap the last element in the left partition with the pivot 
        qqsort(array, start, k - 1); // quicksort the left partition
        qqsort(array, k + 1, end); // quicksort the right partition
    } else { // if there is only one element in the partition, do not do any sorting 
        return;
    }
}

现在您可以看到,此算法始终将第一个元素作为枢轴:int pivot = array[start];

我想修改此算法,使其始终使用子序列的最后一个元素而不是第一个元素,因为我想分析两种实现的物理运行时间。

我尝试将行 int pivot = array[start]; 更改为 int pivot = array[end]; 但算法随后输出了一个未排序的序列:

//Changes: int pivot = array[end]; 
unsorted: {5 4 3 2 1}
*sorted*: {1 2 5 4 3}

为了测试另一个枢轴,我也尝试使用子序列的中心元素,但算法仍然失败:

//Changes: int pivot = array[(start + end) / 2]; 
unsorted: {5 3 4 2 1}
*sorted*: {3 2 4 1 5}

谁能帮我正确理解这个算法,并告诉我需要做哪些改变才能成功地让这个实现总是选择子序列的最后一个元素作为枢轴?

【问题讨论】:

  • 你的牙套发生了一些奇怪的事情。似乎有些被注释掉了,有些被删除了,还有一个留在里面。尝试重新发布您的代码。
  • @colithium - 谢谢你……没注意到;现在修好了。

标签: algorithm sorting pivot quicksort


【解决方案1】:

问题的原因

问题是你使用int k = end;。当您将枢轴元素作为数组中的第一个元素时,使用int i = start; 很好,因为您在循环中的检查将略过它(array[i] &lt;= pivot)。但是,当您使用最后一个元素作为枢轴时, k 会在结束索引处停止并将枢轴切换到分区左半部分的位置。您已经遇到了麻烦,因为您的支点很可能位于左侧分区内部的某个位置,而不是边界。

解决方案

要解决此问题,您需要在使用最右边的元素作为枢轴时设置int k = end - 1;。您还需要更改将枢轴交换到左右分区之间边界的线:

swap(array, i, end);
qqsort(array, start, i - 1);
qqsort(array, i + 1, end);

您必须为此使用 i ,因为 i 最终会出现在右侧分区的最左侧元素(然后可以与位于最右侧元素中的枢轴交换,它会保留顺序)。最后,您需要将k &gt;= i 更改为k &gt; i,同时减少k,否则数组[-1] 索引错误的变化很小。这在以前是不可能发生的,因为此时 i 总是至少等于 i+1。

应该可以的。

旁注:

这是一个写得很糟糕的快速排序,我不建议学习它。它有一些无关紧要的、不必要的比较以及其他一些我不会浪费时间列出的错误。我建议使用 Sedgewick 和 Bentley 在this presentation 中的快速排序。

【讨论】:

  • 感谢 Sedgewick 和 Bentley 的链接 - 很棒的东西。
【解决方案2】:

我没有测试它,但还是检查它:

这个

// after the indices have crossed,
// swap the last element in the left partition with the pivot
swap(array, start, k);

应该是

swap(array, end, i);

或类似的东西,如果我们选择end 作为枢轴。

编辑:这是一种有趣的分区算法,但它不是标准算法。

嗯,pivot 在分区的逻辑中是固定的。
该算法将第一个元素视为 Head,其余元素视为要划分的 Body。
分区完成后,作为最后一步,头部(pivot)与左侧分区部分的 last 元素交换,以保持排序。

我想在不改变算法的情况下使用不同的枢轴的唯一方法是:

...
if (end - start >= 1) {
    // Swap the 1st element (Head) with the pivot
    swap(array, start, pivot_index);
    int pivot = array[start];
...

【讨论】:

  • 我确实尝试过这种组合,但没有奏效。我用array[start]array[end] 尝试过。
【解决方案3】:

第一个提示:如果数据是随机的,则平均而言,您选择哪个值作为枢轴并不重要。真正提高枢轴“质量”的唯一方法是采用更多(例如 3 个)索引并使用具有这些中值的索引。

第二个提示:如果你改变了pivot值,你也需要改变pivot index。这没有明确命名,但array[start] 在某一时刻被交换到已排序子序列的“中间”。您需要相应地修改此行。如果取一个不在子序列边缘的索引,则需要先将其交换到边缘,然后再进行迭代。

第三个提示:你提供的代码注释过多。你应该能够真正理解这个实现。

【讨论】:

    【解决方案4】:

    放一个

    swap(array, start, end)
    

    在初始化枢轴之前

    int pivot = array[start]
    

    【讨论】:

      【解决方案5】:
      #include <time.h>
      #include <stdlib.h>
      #include<iostream>
      #include<fstream>
      using namespace std;
      
      int counter=0;
      void disp(int *a,int n)
      {
      for(int i=0;i<n;i++)
      cout<<a[i]<<" ";
      cout<<endl;
      }
      
      void swap(int a[],int p,int q)
      {
      
      int temp;
      temp=a[p];
      a[p]=a[q];
      a[q]=temp;
      
      }
      
      int partition(int a[], int p, int start, int end)
      {
      swap(a,p,start);// to swap the pivot with the first element of the partition
      counter+=end-start;  // instead of (end-start+1)
      int i=start+1;
      for(int j=start+1 ; j<=end ; j++)
      {
          if(a[j]<a[start])
          {
              swap(a,j,i);
              i++;
      
          }
      
      
      }
      swap(a,start,i-1);  // not swap(a,p,i-1) because p and start were already swaped.....    this was the earlier mistake comitted
      return i-1; // returning the adress of pivot
      }
      
      void quicksort(int a[],int start,int end)
      {
      if(start>=end)
          return;
      
      
      int p=end; // here we are choosing last element of the sub array as pivot
      // here p is the index of the array where pivot is chosen randomly
      
      int index=partition(a,p,start,end);
      
      quicksort(a,start,index-1);
      quicksort(a,index+1,end);
      
      }
      
      int main()
      {
      
      ifstream fin("data.txt");
      int count=0;
      
      int array[100000];
      
      while(fin>>array[count])
      {
          count++;
      }
      quicksort(array,0,count-1);
      /*
      int a[]={32,56,34,45,23,54,78};
      int n=sizeof(a)/sizeof(int);
      disp(a,n);
      
      quicksort(a,0,n-1);
      disp(a,n);*/
      cout<<endl<<counter;
      return 0;
      
      }
      

      【讨论】:

        【解决方案6】:

        如果您开始监视从数组的第一个元素到最后一个元素 - 1 的每个元素,并在每次递归时将最后一个元素作为枢轴,那么您将得到准确的答案 O(nlogn)强>时间。

        #include<stdio.h>
        void quicksort(int [], int, int);
        int main()
        {
        int n, i = 0, a[20];
        scanf("%d", &n);
        while(i < n)
        scanf("%d", &a[i++]);
        
        quicksort(a, 0, n - 1);
        i = 0;
        while(i < n)
        printf("%d", a[i++]);
        
        }
        void quicksort(int a[], int p, int r)
        {
        int i, j, x, temp;
        if(p < r)
        {
            i = p;
            x = a[r];
            for(j = p; j < r; j++)
            {
                if(a[j] <= x)
                {
                    if(a[j] <a[i])
                    {
                        temp = a[j];
                        a[j] = a[i];
                        a[i] = temp;
                    }
                    i++;
                }
        
                else
                {
                    temp = a[i];
                    a[i] = a[j];
                    a[j] = temp;
                }
            }
            if(x != i)
            {
                temp = a[r];
                a[r] = a[i];
                a[i] = temp;
            }
        
            quicksort(a, p, i - 1);
            quicksort(a, i + 1, r);
        }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-01-21
          • 2014-10-25
          • 2014-12-23
          • 2016-03-29
          • 2019-11-23
          • 1970-01-01
          • 2017-02-25
          • 2011-08-31
          相关资源
          最近更新 更多