假设我们已经知道快速排序的算法框架时,我们已经可以从宏观地去掌握快速排序算法的思想。但是快速排序算法的关键还是在于划分操纵,同时快速排序的性能主要取决于划分操作的好坏。快速排序分治partition过程有两种方法:
1)两个下标分别从首、尾向中间扫描的方法
2)两个指针索引一前一后逐步向后扫描的方法
因为我常用第一种,因此,我拿第一种来进行分析,就以下这段代码分析:
1 int Partition(ElemType R[],int low,int high) 2 { 3 ElemType pivot = R[low];//保存枢轴 4 while(low<high) 5 { 6 while(low<high&&R[high]>=pivot)//从右边寻找小于枢轴的值 7 --high; 8 R[low]=R[high]; 9 while(low<high&&R[low]<=pivot)//从左边寻找大于枢轴的值 10 ++low; 11 R[high]=R[low]; 12 } 13 A[low]=pivot;//基准记录已被最后定位 14 return low;//return high也是可以 15 }
每次调用一次快速排序,都要进行划分。而每次划分时候,在划分函数中将发生以下事情:
1、从传进函数的数组中,取一个数作为枢轴(假设是low下标指向的元素),同时声明一个变量来保存枢轴,我们可以认为我们已经从数组中抽掉一个元素。此时,数组中low位置认为为空。这就给下面的循环创造条件。
对应函数中的语句:
ElemType pivot = R[low];//保存枢轴
2、这是一个循环体(循环(1)与(2)),直到不满足low<high跳出:
(1)因为我们假设枢轴元素为low,那么首次就先从high开始向前面扫描,寻找一个小于枢轴 pivot 的元素。当找到的时候,将high指向的元素调到low处空位子,此时high的位置视为空位子。
while(low<high&&R[high]>=pivot)//从右边寻找小于枢轴的值 --high; R[low]=R[high];//填补low位上的空缺
(2)然后,从左向右扫描,寻找大于枢轴 pivot的元素。当找到时候,将low指向的元素调到(1)留下的空位high。此时low处为空。
while(low<high&&R[low]<=pivot)//从左边寻找大于枢轴的值 ++low; R[high]=R[low];//填充high处空缺
3、当上述循环结束后,原来程序试图填补那个空缺最后还是没填上,有一种捉襟见肘的感觉。而这个时候,在中间的那个空位置,左边的数全部小于右边的数。这个空位子理所当然就是枢轴所属,因此完成了一次划分,枢轴元素在本次划分中排在了自己最终的位置上。
4、此时,high与low都指向pivot位置,因此返回给快排函数,最为快排算法中递归调用的新的边界。
A[low]=pivot;//基准记录已被最后定位 return low;//return high也是可以
还有,我们在划分的过程中,所有与枢轴相同的元素不会移动。
第二种方法有待总结。