[算法]快速排序与随机快速排序

前言

与归并排序一样,快速排序也运用了分治策略,而随机快速排序是对快速排序的一种优化。

快速排序

在归并排序中,我们通过下标 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;
}

本文是学习《算法导论》的学习笔记,如有错误,希望大佬在评论区指正!

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值