QuickSort分区

时间:2011-11-16 14:28:50

标签: performance algorithm sorting quicksort

我试图从这个web site了解快速排序算法,paul的实现与stl :: sort一样快(在大范围内快速排序,在较小范围内插入排序)。

我将保罗的实施与我的比较,我的实施慢了3倍。通过分析我们的代码,我发现主要的不确定性是分区

以下是保罗代码的摘录:

void Partition(int data[], int low , int high){
 int pivot = data[low];
 int fromLow = low;
 int fromHigh = high;
 int ptr = low;

 goto QStart;
 while(true){
    do {
        fromHigh--;
        if(fromLow >= fromHigh)
          goto QEnd;
QStart:;        
    }while(data[fromHigh] > pivot)

    data[ptr] = data[fromHigh];
    ptr = fromHigh;

    do{
        fromLow++;
        if(fromLow >= fromHigh){
          ptr = fromHigh;
          goto QEnd;
        }
    }while(data[fromLow] < pivot)
    data[ptr] = data[fromLow];
    ptr = fromLow;
 }
QEnd:;
   data[ptr] = pivot;
}

以下是我的:

void MyPartition(int data[], int low , int high){

 int pivot = data[low]; 
 int fromLow = low;
 int fromHigh = high;
 int ptr = low;
 while(fromLow != fromHigh){
    if(data[fromHigh] >= pivot)
        fromHigh--;
    else{
        data[ptr] = data[fromHigh];
        ptr = fromHigh;

        while(data[fromLow] <= pivot && fromLow != fromHigh)
            fromLow ++;
        data[ptr] = data[fromLow];
        ptr = fromLow;
    }
 }
 data[ptr] = pivot;
}

这两个函数实现相同的算法,我相信它们具有相同的BigO:

  1. 首先,扫描阵列从高端到低端(右=>左) 找到第一个值小于枢轴。
  2. 然后,扫描阵列从低端到高端(左=>右) 找到第一个值大于pivot。
  3. 在任一扫描中,如果发现任何事情,那么我们“交换它 使用透视值“,这是逻辑交换,因为透视值是 使用变量 pivot 缓存,我们可以将变量 ptr 视为 当前枢轴值位置。
  4. 有人知道为什么保罗的执行速度比我快吗?

    更新

    int PartitionData2(int * data, int low, int high){
      int pivot = data[low]; 
      int fromLow = low;
      int fromHigh = high;
      int ptr = low;
    
      while(fromLow < fromHigh){      
        if(data[fromHigh] > pivot)           // '>=' ==> '>'
          fromHigh--;   
        else{
          data[ptr] =data[fromHigh] ;
          ptr = fromHigh;      
    
          while(data[++fromLow] < pivot &&   // '<=' ==> '<'
                fromLow != fromHigh);
    
          data[ptr] = data[fromLow];
          ptr = fromLow;
          fromHigh--;                        // antti.huima's idea
        }
      }
      data[ptr] = pivot;
      return ptr;
    }
    

    我只是根据antti.huima的想法更新代码,这使得我的代码与保罗代码一样快。

    这让我感到困惑,因为它看起来像交换价值等于转移。

    UPDATE2 : 我理解为什么我们需要移动元素等于枢轴,因为如果我们不这样做,那么两个新的分区将是不均匀的,例如应该有一个比另一个大得多。它最终会转到O(n ^ 2)的情况。

    请参阅this PDF

1 个答案:

答案 0 :(得分:2)

您的代码中有一些冗余检查,而paul的代码没有。

例如在行

   while(data[fromLow] <= pivot && fromLow != fromHigh)

第一次检查在第一次迭代时是多余的,因为它总是认为当你开始这次迭代时,在第一次迭代时,数据[fromLow]不高于pivot。原因是每当你开始这个迭代时,你刚刚从'fromHigh'交换了一个小于枢轴的值。因为对于一个随机排序的数组,这个迭代只运行几个循环(它以50%的概率终止随机数),实际上你做了25%的额外比较,而paul的代码没有进行数据透视比较在限制检查之前,但从第一次开始增加。

你在第一个循环中有相同的性能错误,即使它在语法上不同,你也会从高处减少。保罗的代码没有...这就是他需要转到QStart的原因:))