我试图从这个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:
有人知道为什么保罗的执行速度比我快吗?
更新:
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
答案 0 :(得分:2)
您的代码中有一些冗余检查,而paul的代码没有。
例如在行
while(data[fromLow] <= pivot && fromLow != fromHigh)
第一次检查在第一次迭代时是多余的,因为它总是认为当你开始这次迭代时,在第一次迭代时,数据[fromLow]不高于pivot。原因是每当你开始这个迭代时,你刚刚从'fromHigh'交换了一个小于枢轴的值。因为对于一个随机排序的数组,这个迭代只运行几个循环(它以50%的概率终止随机数),实际上你做了25%的额外比较,而paul的代码没有进行数据透视比较在限制检查之前,但从第一次开始增加。
你在第一个循环中有相同的性能错误,即使它在语法上不同,你也会从高处减少。保罗的代码没有...这就是他需要转到QStart的原因:))