**************************转载请注明出处!******************************
Qsort快排,顾名思义,它是排序算法,而且它快,是吧。那这个快从何说起呢?写到这我才发现,说前面几个算法的时候,对时效只字未提,0.0 证明昊昊的确是灰常不合格的程序猿。插入,选择,冒泡,bla bla~我们把它们看做O(n^2)的算法,这里的这个O是个渐进记号,表示的是渐进上界,当然还有一种Θ,它同时渐进地给出了函数的上界及下界。严谨的判断方法当然应该讲算法运行步数表示成一个跟输入规模n有关的函数。当然,精确地计算看起来很麻烦,当n→∞时,许多项可以忽略,再把系数啥的忽略了,就得到上面的O()了(好像是这么回事,这就是我当初在概论里没说这事儿的原因,我自己也不是啥细节都清楚)。而Qsort的效率就要高很多,平均O(nlogn)。不随机的情况下,最坏情况貌似也是O(n^2)。下面就进入正题:qsort!
基本描述
首先要说的是,快排也是基于分治思想的。它分为几个步骤,首先对要排序数组划分(先找出一个pivot(不知道该肿么翻,总之是用来作为比较的标准),将比pivot小的挪到pivot的一侧,其他到pivot的另一侧)。然后再递归地用qsort来排序pivot两边的数组。边界可以设成一个数、两个数,也可以设成小于某个量的时候该用插入排序之类的,它们开销比较小,在小规模的时候不逊于快排。
实现
我们把分划的那一步单独拿出来,称作PARTITION(A, p, r),它返回的是pivot的坐标。假设我们已经完成了这个函数,我们来看一看快排要怎样完成工作,伪代码如下:
QUICKSORT(A, p, r)
If p<r
Then q ← PARTITION(A, p, r)
QUICKSORT(A, p, q-1)
QUICKSORT(A, q+1, r)
看起来好简洁是么,bingo~qsort本来就不是很难,只是好多同学刚开始学的时候,没有很好的接受递归、分治的思想,接受之后这些都是理所当然了哈~仔细来看一下。上面这个伪代码对边界没有特别处理,指针交叉了就完事儿。在工作时(p为左边界,r为又边界),分划过后,q得到pivot的位置。此时数组的情况是,q左边的都小于pivot,q右边的都大于pivot(假设从小到大)。排序p到r数组的问题,就成功的被分解成了排序p到q-1数组以及排序q+1到r数组。不断分划,数组长度越来越短,当p == r时,q == p == r。那么再向下调用QUICKSORT()时就停止了。循环不变的证明就不抄了,我自己是写不出来那么严谨的证明,详细证明大家可以google之类的查查资料吧。
接下来就是算法中关键的部分,PARTITION分划!伪代码如下:
PARTITION1(A, p, r)
X ← A[r]
I ← p-1
For j ← p to r-1
Do if A[j] <= x
Then i ← i+1
Exchange A[i] ↔ A[j]
Exchange A[i+1] ↔ A[r]
Return i+1
注意这是PARTITION1,我常用的不是这个,不过既然《导论》先介绍它,我也把它放到前面来说吧。上来两个变量,X I。X作为pivot使用,而i则是分界线,从左向右扫。工作时,j左侧是已经分划的一部分,其中i左侧的数小于X,当j扫到r-1时,i左侧都小于X,所以将A[i+1] 与 A[r]交换,此时,就分划完成了。同样,这不是严格的循环不变式证明,只是大体有个框框,说明一下这个分划的正确性。
PARTITION2(A, p, r)
X ← A[p]
I ← p-1
J ← r+1
Whie TRUE
Do repeat j ← j – 1
Until A[j] <= x
Repeat I ← I + 1
Until a[i] >= x
If I < j
Then exchange A[i] ↔ A[j]
Else return j
这好像也是最初的那个版本0.0。首先X是pivot,毫无疑问。然后左右指针分别是I j。左右指针分别向中间扫,j扫到一个小于等于X的停下,i扫到一个大于等于X的停下。俩指针交换一下继续,知道指针交叉,返回j。
举例(PARTITION1)
1) 2 8 7 1 3 5 6 |4
I p,j r
2) 2| 8 7 1 3 5 6 |4
I,p j r
3) 2| 8| 7 1 3 5 6 |4
I,p j r
4) 2| 8 7| 1 3 5 6 |4
I,p j r
5) 2 1| 7 8| 3 5 6 |4
p i j r
6) 2 1 3| 8 7| 5 6 |4
p i j r
7) 2 1 3| 8 7 5| 6 |4
p i j r
8) 2 1 3| 8 7 5 6| 4
p i r
9) 2 1 3| 4| 7 5 6 8
p i r
好吧,我只能尽力做成这样了0.0我觉得大家应该能看懂吧。每个步骤包含两行,第一行是当前数组状态,第二行是各个指针状态。”|”将数组分割成若干段。最右边的”|”表示j只能扫到6,而不会扫到最右的数。然后最左面的”|”跟着i,意味着它左边的数都小于4。中间的”|”跟着j,就是说它左边的都已经分划好了。9)处,4已经找到正确位置,左”|”的左侧都小于4,右”|”的右侧都大于4。此时就可以返回i+1了,正好是4(pivot)的位置。
快排应该算是一个非常重要的算法,估计咱们考试也会考到吧~大家还是自己亲手敲一敲代码体会体会吧。哈哈~
http://www.cs.usfca.edu/~galles/visualization/ComparisonSort.html
之前提到过的,这里有可视化的快排。
主页里分享了形象的快排视频。大家可以去看一下,主页菌感觉非常棒。
新学期开始了,主页菌祝大家算法数据结构能那个好成绩,嘎嘎~~