快速排序算法总结
看到很多计算机初级面试得知,基本算法的重要性。尤其是快排,灵机一动,好好整理一下吧。
首先看一个来自于优酷的快速排序算法演示:
http://v.youku.com/v_show/id_XMzMyODk4NTQ4.html
话说真心有点儿不理解,国外的亲们真是有意思,这舞蹈一跳,丫的瞬间就明白了所谓的快速排序。建议国内教授计算机算法的老师都看一下这个系列的舞蹈,很给力。
话说已经有了充分的分析,以下文章转自:http://chenzehe.iteye.com/blog/481188
快速排序的时间主要耗费在划分操作上,对长度为 k 的区间进行划分,共需 k-1 次关键字的比较。
最坏时间复杂度:最坏情况是每次划分选取的基准都是当前无序区中关键字最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的另一个非空的子区间中记录数目,仅仅比划分前的无序区中记录个数减少一个。因此,快速排序必须做 n-1 次划分,第 i 次划分开始时区间长度为 n-i-1, 所需的比较次数为 n-i(1<=i<=n-1), 故总的比较次数达到最大值 Cmax =n(n-1)/2=O(n^2) 。如果按上面给出的划分算法,每次取当前无序区的第 1 个记录为基准,那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中关键字最小(或最大)的记录,则快速排序所需的比较次数反而最多。
最好时间复杂度:在最好情况下,每次划分所取的基准都是当前无序区的“中值”记录,划分的结果与基准的左、右两个无序子区间的长度大致相等。总的关键字比较次数为 O(n×lgn)。
用递归树来分析最好情况下的比较次数更简单。因为每次划分后左、右子区间长度大致相等,故递归树的高度为 O(lgn), 而递归树每一层上各结点所对应的划分过程中所需要的关键字比较次数总和不超过 n,故整个排序过程所需要的关键字比较总次数 C(n)=O(n×lgn) 。因为快速排序的记录移动次数不大于比较的次数,所以快速排序的最坏时间复杂度应为 O(n^2 ),最好时间复杂度为 O(n×lgn)。
基准关键字的选取:在当前无序区中选取划分的基准关键字是决定算法性能的关键。 ① “三者取中”的规则,即在当前区间里,将该区间首、尾和中间位置上的关键字比较,以三者之中值所对应的记录作为基准,在划分开始前将该基准记录和该区的第 1 个记录进行交换,此后的划分过程与上面所给的 Partition 算法完全相同。 ② 取位于 low 和 high 之间的随机数 k(low<=k<=high), 用 R[k] 作为基准;选取基准最好的方法是用一个随机函数产生一个位于 low 和 high 之间的随机数 k(low<=k<=high), 用 R[k] 作为基准 , 这相当于强迫 R[low..high] 中的记录是随机分布的。用此方法所得到的快速排序一般称为随机的快速排序。随机的快速排序与一般的快速排序算法差别很小。但随机化后,算法的性能大大提高了,尤其是对初始有序的文件,一般不可能导致最坏情况的发生。算法的随机化不仅仅适用于快速排序,也适用于其他需要数据随机分布的算法。
平均时间复杂度:尽管快速排序的最坏时间为 O(n^2 ), 但就平均性能而言,它是基于关键字比较的内部排序算法中速度最快的,快速排序亦因此而得名。它的平均时间复杂度为 O(n×lgn)。
空间复杂度:快速排序在系统内部需要一个栈来实现递归。若每次划分较为均匀,则其递归树的高度为 O(lgn), 故递归后所需栈空间为 O(lgn) 。最坏情况下,递归树的高度为 O(n), 所需的栈空间为 O(n) 。
稳定性:快速排序是非稳定的。
下面我来举一个例子:
题目描述:
给予n个数字,输入n,下面有n行,对下面的n个数字从小到大排序,运用快排思想
代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int a[1000000];
int pp(int l,int r)
{
int i=l;
int j=r;
int x=a[l];
while(i<j)// 结束递归
{
while(i<j && a[j]>=x) j--;// 从右向左找第一个小于x的数
a[i]=a[j];
while(i<j && a[i]<=x) i++;// 从左向右找第一个大于x的数
a[j]=a[i];
}
a[i]=x;
return i;
}
void paixu(int l,int r)
{
if(l>=r) return;
int x=pp(l,r);
paixu(l,x-1);
paixu(x+1,r);
}
int main()
{
int n,i;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
paixu(0,n-1);
for(i=0;i<n;i++)
{
if(i==0)
printf("%d",a[i]);
else
printf(" %d",a[i]);
}
printf("\n");
}
return 0;
}
运用STL中的qsort()排序:
代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int a[100000];
int n;
int cmp(const void *a,const void *b)
{
return *(int *)a-*(int *)b;
}
int main()
{
int i,j;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
qsort(a,n,sizeof(a[0]),cmp);
for(i=0;i<n;i++)
{
if(i==0)
printf("%d",a[i]);
else
printf(" %d",a[i]);
}
printf("\n");
}
}
运用STL中的sort()排序:
代码如下:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int a[100000];
int n;
int main()
{
int i,j;
while(scanf("%d",&n)!=EOF)
{
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
sort(a,a+n);
for(i=0;i<n;i++)
{
if(i==0)
printf("%d",a[i]);
else
printf(" %d",a[i]);
}
printf("\n");
}
}