quickSort,就如他的名字一样,他是一种快速的分而治之的算法,即运用了分治的思想,将一个大问题,分割成几个不相交的问题,采取各个击破的办法来进行问题的解决。
刚接触这个算法的时候,记得老师说过的印象最深的一句话就是:quickSort算法实际上就是一个归位的过程。怎么理解呢?
我们先来看一下问题定义:
问题定义:
输入: 数组x【0...n-1】
输出: 有序数组x【0...n-1】
对于输入的数组x【0...n-1】当然了,这个肯定是无序的了,那么quickSort是怎么把他变成一个有序数组的呢?
我们选取x【0...n-1】中的一个数tmp作为基准,当我进行扫描数组的时候,将小于tmp的全部放到tmp的前面,而大于tmp的全部放到tmp的后面,这样的话我们将数组分成了三部分
1.大于tmp的部分。
2.等于tmp的部分。
3.小于tmp的部分。
显然1和3是被分成的两个更小的不相交的子问题,然后我们就可以利用分而治之的思想采用递归来进行问题的解决。而第二部分已然是我们想要的结果,无需做过多的操作,也就是上面所说的,我已经把他给归位了。
那接下来的问题就集中在两方面:
1.怎么选取这个tmp
1.通常的考虑是选择数组的第一个数作为tmp来划分数组
2.随机选取模式,这样会避免最坏情况的发生。
2.如何用tmp来进行数组划分
这里参考介绍两种方法:
伪代码1:quickSort(x[],l,u)
if l>=u then return ;
/*
* 根据特定的值,划分数组,最终将该值放到正确的位置p;
*/
m = l
for i = [l+1,u]
if(x[i] < x[l])
swap(++m,i)
swap(l,m);
quickSort(x[],l,p-1);
quickSort(x[],p+1,u);
伪代码2:quickSort(x[],l,u)
if l>=u then return ;
/*
* 根据特定的值,划分数组,最终将该值放到正确的位置p;
*/
t = x[l]; i = l; j = u+1;
loop
do i++; while(i<=u && x[i]<t)
do j--; while(x[j] > t)
if i>j
break
temp = x[i]; x[i] = x[j]; x[j] = temp;
swap(l,j)
quicksort(x,l,j-1)
quicksort(s,j+1,u)
从直观上来说第二种方法更好理解写,我当初理解第一种方法的时候是试着模仿整个循环过程才理解的。
效率分析:顾名思议,快排,意思也就是说他很快了,该算法平均时间复杂度是O(n*logn),最坏的情况下是O(n^2)
1.最坏情况分析:从整个算法的流程来看,如果输入数组已经是非降序排列的了,那门每次调用最小的元素总是作为基数,结果调用了quickSort(x,1,n),quickSort(x,2,n).......quickSort(x,n,n),在分析其中的划分过程可知,总的比较次数为:(n-1)+(n-2)+(n-3)+...+1+0 = n(n-1)/2 = O(n^2);如果碰到了这种情况,效率确实也退化到了类似于选择排序等的地步,导致这种结果的是每次基数的位子是固定的,在前面提到了还有一种选择方式,即随机选择即从数组中随机选择一个数作为基数 swap(l,randomint(l,u))。这样的话,在一个大型的数据中,如1 000 000 000 数据,就很难出现最坏情况了,如果出现了的话,就证明你可以去买彩票了。如果你运气在好一点每一次选到的都是中项的话,那门递推关系式为C(n) = 2 C(n/2) + O(n),递推式解C(n) = O(n*logn)
2.平均情况分析:考虑几方面:对于一个数据集合,每一种数据组合的概率分布式一样的,也就是每个排列出现的概率是等可能的。 (组合数学没学好啊,递推过程看的很模糊。。。。)最后求出来的结果是C(n) = 1.44nlogn = O(nlogn);
参考:1.Programming Pearls
2.算法设计技巧与分析
3.数据结构与问题求解(c++版)