《算法导论》第7章-快速排序(含C++代码)

7.1 快速排序的描述

一、基本思路

1、采用思想:分治思想。
2、操作:
(1)分解:将数组A[p…r]划分为两个数组A[p…q-1]和A[q+1…r],使得A[p…q-1]的每一个元素都小于A[q],同时A[q]也小于A[q+1…r]中每个元素。
计算下标q也是划分过程的一部分。
(2)解决:通过递归调用快速排序,分别对A[p…q-1]和A[q+1…r]进行排序。
(3)合并:由于子数组都是按照原址排序的,因此不需要合并操作,即数组A[p…r]已经有序。
3、伪代码:

QUICKSORT(A,p,r)
1 if p < r
2     q = PARTITION(A,p,r)
3 	   QUICKSORT(A,p,q-1)
4 	   QUICKSORT(A,q+1,r)

为了排序整个数组A,初始调用是QUICKSORT(A,1,A.length)

二、细节

算法的关键部分在于PARTITON过程,其实现了对子数组A[p…r]的原址重排:

1、PARTITION的伪代码

PARTITION(A,p,r)
1 x = A[r]				//将A[r]作为主元
2 i = p - 1				
//通过循环,使当A[j]<=x时,A[j]移动到最前面一部分
3 for j = p to r-1		
4 		if A[j] <= x
5 			i = i + 1
6			exchange A[i] with A[j]
//最终将主元和A[i+1]互换(即到上面所说的A[q]上)
7 exchange A[i+1] with A[r]
8 return i+1

PARTITION在子数组A[p…r]上的时间复杂度为θ(n),其中n = r - p + 1,主要取决于for循环遍历的次数。

2、通过案例解释PARTITION过程

在这里插入图片描述
图1
在这里插入图片描述
图2
解释:数组项A[r] 是主元x,浅阴影部分的数组元素为第一部分(对应图2的1),深色阴影部分数组对应划分的第二部分(对应图2的2),白色元素就是主元x。
(a):初始的数组和变量设置,数组元素还没有被分配。
(b):2与它自身进行交换(因为自己属于第一部分,且i和j相同)。
(c)~(d):8和7先后被加入到元素值较大的部分之中。
(e):1和8交换,将1移动到元素较小的部分中。
(f):3和7交换,同(e)。
(g)~(h):5和6进入较大的部分。
(i)将主元移动到两个部分中间,从而完成PARTITION。

7.2 快速排序的性能

一、最坏情况划分

1、发生条件:最坏情况出现在当划分产生的两个子问题分别包含n-1个元素和0个元素时;假设每次递归调用都出现了这种不平衡的划分,且每次划分操作的时间复杂度为θ(n)。
2、对于大小为0的数组递归调用会直接返回,T(0) = θ(1);对于大小为n-1的元素,就用T(n-1)表示。
算法时间运行递归式可以表示为
T(n) = T(n-1) + T(0) + θ(n)

二、最好情况划分

1、发生条件:两个子问题的规模都不大于1/2,即一个是⌊n/2⌋,另一个是⌈n/2⌉-1
2、此时算法的运行时间是
T(n) = 2T(n/2) + θ(n)
根据主定理,上述递归式的解为T(n) = θ(lg n)

三、平衡的划分

1、举个例子,假设划分的比例是9:1,那么算法时间复杂度的递归式为:
T(n) = T(9n/10) + T(n/10) + cn,递归树如下
在这里插入图片描述
图3 树中每一层的代价均为cn,直到在深度为log10n = θ(lg n)的情况;之后在每一层代价都小于等于cn,因为左半边的树不再向下,这一直到log10/9n = θ(lg n)结束;因此快速排序的运行时间无论如何都是O(n lgn); 事实上,任何一个常数比例的划分都会产生深度为θ(lg n)的递归树,且每次代价为O(n),因此,只要划分是常数比例的,算法的运行时间总是O(nlgn)

7.3 快速排序的随机化版本

一、引入

在讨论快速排序平均性能的时候,我们前提假设是输入数据的所有可能都是等概率的,但是这种情况不一定一直成立,因此我们需要在算法中引入随机性。

二、如何达到随机化

1、采用一种随机抽样法,在A[p…r]中随机抽取一个作为主元,首先将A[r]和A[p…r]中随机抽取出的一个元素进行交换,从而保证主元元素A[r]是从A[p…r]的r-p+1个元素中抽取的。
2、新的伪代码

PARTITION(A,p,r)
1 x = A[r]				//将A[r]作为主元
2 i = p - 1				
//通过循环,使当A[j]<=x时,A[j]移动到最前面一部分
3 for j = p to r-1		
4 		if A[j] <= x
5 			i = i + 1
6			exchange A[i] with A[j]
//最终将主元和A[i+1]互换(即到上面所说的A[q]上)
7 exchange A[i+1] with A[r]
8 return i+1
RANDOMIZED-PARTITION(A,p,r)
1 i = RANDOM(p,r)						//随机在A[p...r]中抽取一个元素
2 exchange A[r] with A[i]				//将A[r]和抽取的元素进行交换
3 return PARTITION(A,p,r)			//通过之前的PARTITION方法返回结果
RANDOMIZED-QUICKSORT(A,p,r)
1 if p < r
2 	q = RANDOMIZED-PARTITION(A,p,r)//先将原数组分为两部分,下面再分别递归排序
3 	RANDOMIZED-QUICKSORT(A,p,q-1)
4 	RANDOMIZED-QUICKSORT(A,q+1,r)

C++代码

#include <iostream>
using namespace std;
#include <vector>
int Partition(vector<int>& A, int p, int r);
//快速排序
void QuickSort(vector<int>& A, int p, int r)
{
	if (p < r) {
		int q = Partition(A, p, r);
		QuickSort(A, p, q - 1);
		QuickSort(A, q + 1, r);
	}
}
//数组的划分
int Partition(vector<int>& A, int p, int r)
{
	int x = A[r];
	int i = p - 1;
	for (int j = p; j < r; j++) {
		if (A[j] <= x) {
			i++;
			swap(A[i], A[j]);
		}
	}
	swap(A[i + 1], A[r]);
	return i + 1;
}
int main()
{
	vector<int> A;
	cout << "请输入数字总数:";
	int n, m;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> m;
		A.push_back(m);
	}
	QuickSort(A, 0, A.size() - 1);
	for (auto i : A) {
		cout << i << " ";
	}
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KeepCoding♪Toby♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值