堆与堆排序在优先级队列中的应用

1.堆

1.1 堆的定义与基本操作
堆是一颗完全二叉树,数中每个结点的值都不小于(或不大于)其左右孩子的结点的值。其中,如果父亲结点的值大于等于孩子结点的值,那么称这样的堆为大顶堆,这时每个结点的值都是以它为根结点的子数的最大值;如果父亲结点的值小于等于孩子结点的值,那么称这样的堆为小顶堆,这时每个结点的值都是以它为根结点的子数的最小值。堆一般用于优先队列的实现,而优先队列默认使用大顶堆,因此我以大顶堆为例来讲述,以下所指的堆都是指大顶堆
好,那怎么具体实现呢?对于完全二叉树来说,比较简单的是实现方法是用数组来存储完全二叉树。这样结点就按层序存储于数组中,其中第一个结点将存储于数组中的1号位,,并且数组i号位表示的结点的左孩子就是2i号位,而右孩子是(2i+1)号位。于是可以像下面这样定义数组来表示堆:

const int maxn = 10;
int heap[maxn] , n = 10;

而建堆过程事实上是向下调整:,调整方法如下:
总是将当前结点V与它们的左右孩子比较(如果有的话),假如孩子中存在权值比结点V的权值大的,就将其中权值最大的那个孩子结点与结点V交换;交换完毕后继续让结点和其左孩子比较,直到结点V的的孩子的权值都比结点的权值小或是结点V不存在孩子结点。
可以写出向下调整的代码:

//对heap数组在[low,high]范围内进行向下调整
//其中low为欲调整结点的数组下标 high一般为堆的最后一个元素数组的下标
void DownAdjust(int low,int high){
	int i = low , j = i*2 ;//i为欲调整结点 j为其左孩子
	while(j<=high){//存在左孩子 
		//如果右孩子存在且右孩子的值大于左孩子的值 
		if(j+1<=high && heap[j+1]>heap[j]){
			j = j + 1;//让j存储右孩子下标 
		} 
		//如果孩子中最大的权值比欲调整的结点i大
		if(heap[j]>heap[i]){
			swap(head[j],heap[i]);//交换最大权值的孩子与欲调整结点i
			i = j;//保持i为欲调整结点、j为i的左孩子
			j = i*2; 
		}else{
			break;//孩子的权值均比欲调整结点i小 调整结束 
		}
	} 
} 

建堆的代码如下:

//建堆 O(n) 
void CreateHeap(){
	for(int i = n / 2 ; i >= 1 ; i--){
		downAdjust(i,n);
	} 
} 

另外,如果要删除堆中的最大元素(也就是堆顶元素),并让其保持堆的结构,那么只需要最后一个元素覆盖堆顶元素,然后对根结点进行调整即可。

//删除堆顶元素 O(logn) 
void DeleteTop(){
	heap[1] = heap[n--];//用最后一个元素覆盖堆顶元素 并让元素个数减1
	downAdjust(1,n);//向下调整堆顶元素 
}

那么,如果要往堆里添加一个元素,应当怎么办呢?可以把想要添加的元素放在数组最后(也就是完全二叉树的最后一个结点后面),然后进行向上调整操作。向上调整就是把欲调整结点与父亲结点比较,如果权值比父亲结点大,那么就交换其与父亲结点,这样反复比较,直到达到堆顶或是父亲结点的权值较大为止。

//往堆里添加一个元素 
//对heap数组在[low,high]范围进行向上调整 
//和向下调整思路差不多 就是j的赋值有所改变 
//其中low一般设置为1 high表示欲调整结点的数组下标
void UpAdjust(){
	int i = high , j = i / 2;//i为欲调整结点 j为其父亲结点 
	while(j >= low){//父亲在[low,high]范围内 	
		//如果父亲权值小于欲调整结点的值 
		if(heap[j]<heap[i]){
			swap(heap[j],heap[i]);
			i = j;//保持i为欲调整结点、j为i的父亲
			j = i / 2;	
		}else{
			break;
		}//父亲权值比欲调整结点i的权值大 调整结束 
	}
} 

2.堆排序

堆排序是指使用堆结构对一个序列进行排序。此处讨论递增排序的情况。考虑对一个堆来说,堆顶元素是最大的,因此在堆建立完毕后,堆排序的直观思路就是取出堆顶元素,然后将堆的最后一个元素替换至堆顶,再进行依次针对堆顶元素的向下调整-----如此重复,直到堆中只有一个元素为止。
具体实现时,为了节省空间,可以倒着遍历数组,假设当前访问到i号位,那么将堆顶元素与i号位的元素交换,接着在[1,i-1]范围内对堆顶元素进行一次向下调整即可。

//堆排序
void HeapSort(){
	createHeap();//建堆
	for(int i = n;i>1;i--){//倒着枚举 直到堆中只有一个元素 
		swap(heap[i],heap[1]);//交换heap 
		DownAdjust(1,i-1);//调整堆顶 
	} 
} 

参考资料:算法笔记-p335-堆

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值