10.1优先级队列、堆排序

一、优先级队列

  • 功能接口
template <typename T>struct PQ{//Priority Queue
	virtual void insert(T) = 0;//按照优先级次序插入词条
	virtual T getMax() = 0;//取出优先级最高的词条
	virtual T delMax() = 0;//删除优先级最高的词条
};//与其说PQ是数据结构,不如说是ADT;其不同的实现方式,效率及适应场合也各不相同
  • 完全二叉堆Complete Binary Heap(效率兼顾成本)
    • 若要查找极值元,则不必维护所有元素之间的全序关系,偏序足以。
    • 逻辑上,等同于完全二叉树;物理上,直接借助向量实现。
    • 逻辑节点与物理元素依层次遍历次序彼此对应,秩为i的节点
      • 父亲节点:(i - 1) / 2
      • 左孩子:1 + 2 * i
      • 右孩子:(1 + i ) * 2
#define Parent(i) ((i - 1) >> 1
#define LChild(i) (1 + ((i) << 1))//奇数
#define RChild(i) ((1 + (i)) << 1)//偶数
  • 完全二叉堆的实现
//PQ_ComplHeap = PQ + Vector
//以公开的模式,同时继承了PriorityQueue和Vector的特性,即在物理上拥有了一个_elem[]的可扩充数组
template <typename T> class PO_ComplHeap:public PQ<T>, public Vector<T>{
protected:	Rank percloateDown(Rank n, Rank i);//下滤
			Rank percolateUp(Rank i);//上滤
			void heapify(Rank n);//Floyd建堆算法
public:		PQ_ComplHeap(T* A, Rank n)//批量构造
				{copyFrom(A, 0, n); heapify(n);}
			void insert(T);//按照比较器确定的优先级次序,插入词条
			T getMax(){return _elem[0];}//读取优先级最高的词条
			T delMax();//删除优先级最高的词条
};
  • 堆序性

    • 任何一个节点在数值上都不会超过它的父亲
    • 故根节点为最大元
  • 插入与上滤

    • 为插入词条e,只需将e作为末元素接入向量。
      • 结构性自然保持。
      • 若堆序新也亦未破坏,则完成。
    • 上滤
      • 若堆序性被破坏,只能是e与其父节点违反堆序性, 将e与其父节点换位。还要判断e与其新的父亲的堆序性,不断重复,直到根节点。
    • 实现
    • 优化:实现中采用swap函数,则要赋值三次,最坏情况下累计要赋值3logn。针对该情况进行优化,首先将新插入的词条备份,每次如有必要交换,只是下移它的父亲节点,直到能够确定e已经无需上溢时,再将此前备份的词条纳入最终的位置。实现将赋值操作的次数从3logn减少至logn+2.
template<typename T> void PQ_ComplHeap<T>::insert(T e)//
	{Vector<T>::insert(e); percolateUp(_size - 1);}

template<typename T>//对第i个词条实施上滤,i < _size
Rank PQ_ComplHeap<T>::percolateUp(Rank i){
	while(ParentValid(i)){//只要i有父亲(尚未抵达堆顶),则
		Rank j = Parent(i);//将i之父记作j
		if(lt(_elem[i], _elem[j])) break;//一旦父子不再逆序,上滤完成
		swap(_elem[i], elem[j]); i = j;//否则,交换父子位置,并上升一层
	}//while
	return j;//返回上滤最终抵达的位置
}
  • 删除与下滤
    • 最大元素始终在堆顶,故删除之,只需要O(1)
    • 摘除向量的首元素,代之以末元素e
      • 结构性保持
      • 若堆序性依然保持则完成
    • 下滤
      • 若堆序性被破坏,只能是e与孩子们违背了堆序性。将e与孩子中的最大者换位,若堆序性因此恢复,则完成。不断重复直到叶子节点。
    • 效率O(logn)
template <typename T> TPQ_ComplHeap<T>::delMax(){//删除
	T maxElem = _elem[0]; _elem[0] = _elem[--_size];//摘除堆顶,代之以末词条
	percolateDown(_size, 0);//对新堆顶实施下滤
	return maxElem;//返回此前备份的最大词条
}

template <typename T>//对前n个词条中的第i个实施下滤,i<n
Rank PQ_ComplHeap<T>::percolateDown(Rank n, Rank i){
	Rank j;//i及其(至多两个)孩子中,堪为父者
	while(i != (j = ProperParent(_elem, n, i)))//只要i非j,则
	//该循环退出有两种可能,第一种是i大于它的任何一个孩子,第二种是i持续下滤到最底层,并成为一片叶子。
		{swap(_elem[i], _elem[j]); i = j;}//换位,并继续考察i
	return i;//返回下滤抵达的位置
}

二、批量建堆Heapification

  • 定义:将任意n个元素建造成完全二叉堆
  • 自上而下的上滤
    • 最坏情况下,每个节点都要上滤至根,所需的成本线性正比于其深度
    • 即便只考虑底层,n / 2个叶节点,深度均为O(logn),累计耗时O(nlogn)
    • 这样长的时间,足以全排序,可以优化。
PQ_ComplHeap(T* A, Rank n){copyFrom(A, 0, n); heapify(n);}
//以任意指定的规模为n的数组为蓝本,将其中的元素组成一个完全二叉堆。
//先调用向量的copyFrom接口,将这个数组复制到内部,实质的操作则是调用heapify算法将元素调整为堆。

//蛮力算法
template<typename T> void PQ_ComplHeap<T>::heapify(Rank n){
//自上而下,自左向右
	for(int i = 1; i < n ; i++)//按照层次遍历次序逐一
		percolateUp(i);//对每一个元素做上滤处理
}
  • 自下而上的下滤
    • 任意给定堆H0和H1,以及节点p,为得到该三者组成的堆,只需将r0和r1当作p的孩子,对p下滤
    • 实现:Floyd算法
    • 效率
      • 每个内部节点所需的调整时间,正比于其高度而非深度。在渐进意义上每个节点的高度和为O(n)
      • 蛮力算法的效率是对其深度求和。二者之所以有如此大的差异,是因为在完全二叉树中越是靠近底层,节点越多。
template<typename T>
void PQ_ComplHeap<T>::heapify(Rank n){
	for(int i = LastInternal(n); i >= 0; i--)//从最左边叶子节点的父节点处开始,合并子堆。自下而上,自右向左
		percolateDown(n, i);//依次下滤各内部节点
}//可理解为子堆的逐层合并

三、堆排序

  • 流程
    • 初始化:heapify(), O(n)建堆
    • 迭代:delMax(), O(logn),取出堆顶并调整复原
    • 不变性:H<=S,即待排序部分不超过已排序部分
  • 就地实现
    • 已排序部分位于向量的后端,而与之互补的前缀恰好构成一个完全二叉堆。
    • 在大顶堆中,堆中的最大元始终是0号元素,而需要与之对换的x则必然是相对于已排序元素而言秩为-1的那个元素。通过将最大元与x反复的交换、下滤,实现堆变空。
template <typename T>//对向量区间[lo, hi)做就地堆排序
void Vector<T>::heapSort(Rank lo, Rank hi){
	PQ_ComplHeap<T> H(_elem + lo, hi - lo);//待排区间建堆,O(n)
	while(!H.empty())//反复地摘除最大元并归入已排序的后缀,直至堆空
		_elem[--hi] = H.delMax();//等效于堆顶与末元素对换后下滤
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值