我正在尝试从这个网站了解快速排序算法:网站链接,Paul的实现速度与stl::sort(大范围内的快速排序,小范围内的插入排序)一样快。
我将Paul的实现与我的实现进行对比,我的实现比他的慢3倍。通过对我们的代码进行分析,我发现主要差异在于Partition(划分)。
以下是Paul代码的摘录:
以下是我的内容:
这两个函数实现了相同的算法,我认为它们具有相同的 BigO 复杂度:
更新:
我将Paul的实现与我的实现进行对比,我的实现比他的慢3倍。通过对我们的代码进行分析,我发现主要差异在于Partition(划分)。
以下是Paul代码的摘录:
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 复杂度:
- 首先,从高端到低端(从右到左)扫描数组,以寻找第一个小于枢轴的值。
- 然后,从低端到高端(从左到右)扫描数组,以寻找第一个大于枢轴的值。
- 在任一扫描中,如果找到任何东西,则我们将其与枢轴值“交换”,这是逻辑上的交换,因为 枢轴值 被缓存到变量 pivot 中,我们可以将变量 ptr 视为当前的 枢轴值 位置。
更新:
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的思路更新了代码,这使得我的代码和paul的代码一样快。
这让我感到困惑,因为它看起来像是交换与枢轴相等的值。
更新2: 我理解为什么我们需要移动与枢轴相等的元素,因为如果不这样做,两个新的分区将会不平衡,例如一个分区可能比另一个要大很多。最终会导致O(n ^ 2)的情况。
请参见此PDF文档