关于快速排序,有许多版本,均需要掌握:
算法导论中的版本(选取最后一位为主元)
在我写的第二篇文章中,我们已经知道:
“再到后来,N.Lomuto又提出了一种新的版本,此版本….,即优化了PARTITION程序,它现在写在了 算法导论 一书上”:
快速排序算法的关键是PARTITION过程,它对A[p..r]进行就地重排:
PARTITION(A, p, r)
1 x ← A[r] //以最后一个元素,A[r]为主元
2 i ← p - 1
3 for j ← p to r - 1 //注,j从p指向的是r-1,不是r。
4 do if A[j] ≤ x
5 then i ← i + 1
6 exchange A[i] <-> A[j]
7 exchange A[i + 1] <-> A[r] //最后,交换主元
8 return i + 1
然后,对整个数组进行递归排序:
QUICKSORT(A, p, r)
1 if p < r
2 then q ← PARTITION(A, p, r) //关键
3 QUICKSORT(A, p, q - 1)
4 QUICKSORT(A, q + 1, r)
根据上述伪代码,我们不难写出以下的c/c++程序:
首先是,PARTITION过程:
int partition(int data[],int lo,int hi)
{
int key=data[hi]; //以最后一个元素,data[hi]为主元
int i=lo-1;
for(int j=lo;j<hi;j++) ///注,j从p指向的是r-1,不是r。
{
if(data[j]<=key)
{
i=i+1;
swap(&data[i],&data[j]);
}
}
swap(&data[i+1],&data[hi]); //不能改为swap(&data[i+1],&key)
return i+1;
}
补充说明:举个例子,如下为第一趟排序(更多详尽的分析请参考第二篇文章):
第一趟(4步):
a:3 8 7 1 2 5 6 4 //以最后一个元素,data[hi]为主元
b:3 1 7 8 2 5 6 4
c:3 1 2 8 7 5 6 4
d:3 1 2 4 7 5 6 8 //最后,swap(&data[i+1],&data[hi])
算法导论的扩展(选取第一位为主元)
void quickSort(int p, int q)
{
if(p < q)
{
int x = a[p]; //以第一个元素为主元
int i = p;
for(int j = p+1; j < q; j++)
{
if(a[j] < x)
{
i++;
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
int temp = a[p];
a[p] = a[i];
a[i] = temp;
quickSort(p, i);
quickSort(i+1, q);
}
}
前后指针法(选取第一位为主元):
在上面,我们已经知道,Hoare的快速排序版本可以通过前后俩个指针,分别指向首尾,分别比较而进行排序。
下面,分析一下此版本,或其它变种问题:
I、 俩个指针,i指向序列的首部,j指着尾部,即i=1,j=n,取数组中第一个元素ki为主元,即key<-ki(赋值)。
II、赋值操作(注,以下“->”,表示的是赋值):
j(找小),从右至左,不断--,直到遇到第一个比key小的元素kj,ki<-kj。
i(找大),从左至右,不断++,直到遇到第一个比key大的元素ki,kj<-ki。
III、按上述方式不断进行,直到i,j碰头,ki=key,第一趟排序完成接下来重复II步骤,递归进行。
所以,整趟下来,便是这样:
3 8 7 1 2 5 6 4
2 8 7 1 3 5 6 4
2 3 7 1 8 5 6 4
2 1 7 3 8 5 6 4
2 1 3 7 8 5 6 4
2 1 3 7 8 5 6 4
也能够实现同样地效果;