快速排序的经典实现与分析

来源:互联网 发布:php报undefined index 编辑:程序博客网 时间:2024/05/17 21:47

快速排序

快速排序是实际应用中效率最高的排序算法,它的期望时间复杂度是O(nlgn),最坏情况时间复杂度是O(n2)。虽然最坏情况复杂度很高,但是平均期望时间复杂度为O(nlgn),且常数项很小,通过随机抽样的改进后,排序的时间复杂度不会受输入数据的影响。而且算法的空间复杂度为O(lgn),能够进行原址排序。

算法代码如下所示:

int partition(vector<int>& ToBeSorted,int first,int last){ //分解步骤,将数组划分为大于x和小于x两部分,最后将x移位至中央,即x的最终正确位置,在交换元素位置时,有可能导致元素值相同的元素相对位置颠倒,因此需要快速排序是不稳定排序。    static default_random_engine e;    uniform_int_distribution<> u(first,last);    swap(ToBeSorted[first],ToBeSorted[u(e)]);    int pivot = ToBeSorted[first];    int lhsIndex = first,rhsIndex = last + 1;    while(true){        while(ToBeSorted[++lhsIndex]<pivot) if(lhsIndex == last) break;        while(ToBeSorted[++rhsIndex]>pivot) if(rhsIndex == first) break;        if(lhsIndex >= rhsIndex) break;        swap(ToBeSorted[lhsIndex],ToBeSorted[rhsIndex]);            }    swap(ToBeSorted[rhsIndex],ToBeSorted[lhsIndex]);    return rhsIndex;    }}void QuickSort(vector<int>& ToBeSorted,int first,int last){    if (first >= last) return;         //相等说明子数组只有一个元素,无需排序。    int index = partition(ToBeSorted,first,last);    //每次调用Partition后,有ToBeSorted[first...index-1]<=ToBeSorted[index] <=ToBeSorted[index + 1...last],即index位置的元素到达正确位置,之后只需递归排序前半部分和后半部分即可。    QuickSort(ToBeSorted,first,index - 1);    QuickSort(ToBeSorted,index + 1,last);}

上述实现有几个优点,一是随机选择pivot,可以避免对已排序的数组进行排序时,时间复杂度将退化为O(n2);其次是Partition函数中,采用两端向中间递推的方法,可以更高效的交换元素,而且对于有大量相同元素的数组,Partition函数还是能将数组分为较为均匀的两部分(遇到相同元素时,lhsIndex和rhsIndex依然会递进)。

快速排序的平均复杂度分析

由于Partition函数循环时,lhsIndex 和 rhsIndex 只遍历数组一次,且swap函数的复杂度为O(1),因此Partition函数的复杂度为O(n)。对于平均情况下的快速排序效率,我们可以假设Partition函数得到的index的值是随机的,故:

T(n)=n1i=11n1(T(i)+T(ni1))+O(n)=n1i=12n1T(i)+cn

T(n+1)=ni=12nT(i)+c(n+1)

化简有:T(n+1)=n+1nT(n)+2c
初始条件T(1)显然为1,展开T(n)至T(1),有:
T(n+1)=n+1n[nn1[[21T(1)+2c]]+2c=(n+1)T(1)+2c(n+1)(1n+1+1n++11)
根据调和级数近似求和公式有:
T(n)=O(ln(n))

Partition函数的妙用

参见博客xxx.

小结

快速排序实现非常简单,区区十几行代码就能在不利用额外存储空间的情况下,完成高效的排序,但是缺点是对于已排序的数组和大量相同元素值的数组排序时很慢,上文的实现中考虑这两点进行了改进。但是仍然存在稳定性的问题。除此之外,快速排序递归实现会产生很多小数组,每个小数组都要调用Partition函数,降低了效率。解决办法是利用插入排序排序 小数组非常高效的特点,设定一个阈值,当数组大小达到阈值时,调用插入排序。

0 0