前言
与归并排序一样,快速排序也运用了分治策略,而随机快速排序是对快速排序的一种优化。
快速排序
在归并排序中,我们通过下标 n/2 把一个数组分成两个子列,然后递归地进行排序。这种分治策略在快速排序中也同样能够体现。
在快速排序中,我们也想通过某种标准,把一个数组划分为两个子列,然后递归地进行排序。这里与归并排序不同的是:划分子列的策略不同,也就是“分”的标准不同。
假设我们要将数组 A[ n ] 排序为递增序列。
在快速排序中,我们将数组的最后一位 A[ n-1 ] (或第一位,这里使用最后一位) 作为划分依据,从A[ 0 ]开始遍历,如果A[ i ] < A[ n-1 ],那么就把它放在数组的前面。这样遍历下来,如果有 k 个元素小于 A[ n-1 ],那么A[ 0..k-1 ]均为小于A[ n-1 ]的元素,后面的都是大于等于它的元素。这时,数组A[ n ]隐性地显示了两个子列,我们把A[ n-1 ]移动到A[ k ]的位置上( A[ k ] 与 A[ n-1 ] 交换),A[ k ]就相当于两个子列的分界线。动态过程如下:
伪代码如下:
PARTITION( A, p, r )
1 x = A[ r ]
2 i = p - 1
3 for j = p to r - 1
4 if A[ j ] < x i++ swap A[ j ] with A[ i ]
5 swap A[ i + 1 ] with A [ r ]
6 return i + 1
其中,返回的值 i + 1 就是分界线所在的位置,根据左侧元素比它小,右侧元素比它大的性质可知,这个分割线元素的位置是确定的了,不需要再对它进行排序。
我们发现,这种分割方式在分割的同时进行了原址排序,也就是边分边治。因此也不需要合并操作,相较于归并排序,节省了额外的储存空间。
对于每个分割好的子列,又要对其进行分割,直到只剩单一元素这时已经是最小子问题,这与归并排序的分解思路类似。因此,我们可以写出如下代码来实现快速排序:
QUICK-SORT( A, p, r )
1 if p < r
2 q = PARTITION( A, p, r )
3 QUICK-SORT( A, p, q - 1 )
4 QUICK-SORT( A, q + 1, r )
与归并排序类似,同样利用了递归策略实现。为了给数组 A[ n ] 排序,调用QUICK-SORT( A, 0, n-1 ) 即可。
C/C++代码如下:
//分组重排
int Partition(int* a, int p, int r) { //参数:数组,头,尾
int x = a[r]; //取得尾部数据,即作为分割点
int i = p - 1; //i相当于指针,指向比x小的最后一位
for (int j = p; j < r; j++) { //从第一项开始遍历
if (a[j] <= x) { //比x小的就放在前面
i++;
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
a[r] = a[i + 1]; //将a[r]与a[i+1]互换,实现将x放到中间
a[i + 1] = x;
return i + 1; //返回x所在位置
}
//快速排序
void Quick_Sort(int* a, int p, int r) {
if (p >= r) {
return;
}
int q = Partition(a, p, r);
Quick_Sort(a, p, q - 1);
Quick_Sort(a, q + 1, r);
return;
}
随机快速排序
随机化抽样往往可以在大量数据统计的过程中取得一个较优的性能,随机化的快速排序将分割元素随机化,也就是说我们不一定使用两边的元素作为分割,数组中的任何一个元素都有可能作为分割元素。因此,PARTITION操作需要进行改动,我们使用RANDOM-PARTITION进行原址重排。其伪代码如下:
RANDOM-PARTITION( A, p, r )
1 i = random( p, r ) //随机选择一个元素
2 swap A[ r ] with A[ i ] //将随机选择的元素作为末尾
3 return PARTITION( A, p, r ) //继续使用重排操作
想法很简单,这里就不多说了。
同理,QUICK-SORT也需要稍作修改。
RANDOM-QUICK-SORT( A, p, r )
1 if p < r
2 q = RANDOM-PARTITION
3 RANDOM-QUICK-SORT( A, p, q-1 )
4 RANDOM-QUICK-SORT( A, q+1, r )
C/C++代码如下:
//随机化版本
int rand_Partition(int* a, int p, int r) {
int m = rand() % (r - p + 1) + p;
int t = a[m];
a[m] = a[r];
a[r] = t;
return Partition(a, p, r);
}
void rand_QuickSort(int* a, int p, int r) {
if (p >= r) {
return;
}
int q = rand_Partition(a, p, r);
rand_QuickSort(a, p, q - 1);
rand_QuickSort(a, q + 1, r);
return;
}
本文是学习《算法导论》的学习笔记,如有错误,希望大佬在评论区指正!