第二节课(2)

快速排序

划分

  • 给定一个数列A[lo...hi]
  • 重排数列A[lo...hi],使其成为两个子数组(可能为空)A[lo...mi-1]和A[mi+1...hi]

对任何lo \leq u\leq mi-1mi+1\leq v\leq hi,都有A[u]< A[mi]< A[v]

图1

 x称为轴(pivot)。

快速排序的步骤:

  • 先调用划分
  • 递归排序A[lo...mi-1]和A[mi+1...hi]

划分的思想:

使用A[hi]作为轴,从左向右扩展分区。

图2
  •  初始化(i,j) = (lo-1,lo)
  • j每次加1,必要时i加1(当A[j] > x时,只增加j;当A[j] <= x时,还需增加i,并交换i,j号元素)
  • 当j==hi时,结束

 c++实现:

/************************************************************** 
	快速排序一次划分 (Partition) 
	时间复杂度:O(n) 
	备注:[lo,hi]为闭区间 
***************************************************************/
int Partition(vector<int>& v, int lo, int hi)
{
	int p = lo + rand()%(hi-lo+1);	//随机取轴
	//swap v[p] and v[hi] 
	int temp = v[p];
	v[p] = v[hi];
	v[hi] = temp;
	int pivot = v[hi];
	int i = lo - 1;
	for(int j = lo; j < hi; ++j){
		if(v[j] <= pivot){
			++i;
			//swap v[i] and v[j]
			temp = v[i];
			v[i] = v[j];
			v[j] = temp;
		}
	}
	//swap v[i+1] and v[hi]
	temp = v[i+1];
	v[i+1] = pivot;
	v[hi] = temp;	
	return i+1;
}
/******************************************************************
	快速排序 (QuickSort) 
	时间复杂度:O(nlogn) 
	备注:[lo,hi)为左闭右开区间 
*******************************************************************/
void QuickSort(vector<int>& v, int lo, int hi)
{
	if(hi - lo < 2)	//单元素区间自然有序 
		return;
	int mi = Partition(v,lo,hi-1);
	QuickSort(v,lo,mi);
	QuickSort(v,mi+1,hi); 
}

 

堆排序

优先级队列

优先级队列是一种抽象数据结构,支持以下两种操作:

  • 插入(Insert):插入堆新的元素
  • 提取最小值(Extract-Min):从队列中移除并返回最小元素

二叉堆(Binary Heap):完全二叉树

堆序:A[parent(i)] <= A[i](最小堆)

堆的性质:

如果维持好堆序,堆可以高效支持以下两种操作:(假设队中有n个元素)

  • 插入(Insert):O(logn)时间复杂度
  • 提取最小值(Extract-Min):O(logn)时间复杂度

插入(Insertion)

  • 插入新元素到堆末
  • 恢复最小堆的性质(渗透:如果该元素的父节点比该元素更大,交换它们)
  • 正确性:每次交换后,新元素为根的子树满足最小堆的性质
  • 时间复杂度:O(logn)

提取最小值(Extract-Min)

  • 复制最后元素到根
  • 通过下渗透恢复最小堆的性质(如果该元素比它的两个孩子中之一更大,将该元素与较小的孩子交换)
  • 正确性:每次交换后,除了包含该元素的节点外其他所有节点都满足最小堆的性质
  • 时间复杂度:O(logn)

堆排序

  • 构建一个n个元素的二叉堆(一个一个插入n个元素,更高效的方法见《算法导论》)
  • 执行Extract-Min操作n次

c++实现:

/******************************************************************
	堆插入(HeapInsert)
	时间复杂度:O(logn)
	备注1:v为最小堆,k为插入元素
	备注2:堆数组从索引0开始,计算父子索引时略有改变
*******************************************************************/
void HeapInsert(vector<int>& v, int k)
{
	v.push_back(k);
	int i = v.size() - 1;
	int p = (i - 1) / 2;	//父节点索引 
	while (i > 0 && v[p] > v[i]) {
		int temp = v[p];
		v[p] = v[i];
		v[i] = temp;
		i = p;
		p = (i - 1) / 2;
	}
	return;
}
/******************************************************************
	提取最小值(Extract-Min)
	时间复杂度:O(logn)
	备注1:v为最小堆,返回堆顶元素
	备注2:堆数组从索引0开始,计算父子索引时略有改变
*******************************************************************/
int ExtractMin(vector<int>& v)
{
	int r = v[0];
	int n = v.size();
	v[0] = v[n - 1];
	v.pop_back();
	--n;
	int i = 0;
	int j = 2 * i + 1;	//左子索引 
	while (j < n) {	//存在左子 
		if (j < n - 1 && v[j] > v[j + 1])	//如果右子存在且较小
			++j;

		if (v[i] < v[j])
			break;
		else {
			int temp = v[i];
			v[i] = v[j];
			v[j] = temp;
			i = j;
			j = 2 * i + 1;
		}
	}
	return r;
}
/******************************************************************
	堆排序(HeapSort)
	先通过n次插入方式建堆,再n次提取堆顶元素排序
	时间复杂度:O(nlogn)
*******************************************************************/
void HeapSort(vector<int>& v)
{
	int n = v.size();
	vector<int> t;
	for (int i = 0; i < n; ++i)
		HeapInsert(t, v[i]);	//建堆
	for (int i = 0; i < n; ++i)
		v[i] = ExtractMin(t);	//提取堆顶元素
	return;
}

基于比较的排序算法的时间复杂度下界

基于比较的排序算法的时间复杂度下界:\Omega (nlogn)。(证明:决策树的高度)

 

最快的排序算法

Which algorithm is the best in practice?

图3 运行效果

三种排序算法的运行时间对比可知:实际中,快速排序的用时最短,但与STL中实现的差距很大,可优化的空间还很大。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值