《数据结构》学习笔记-第十章 优先级队列(call-by-priority))


stack(LIFO)和queue(FLFO)都是PQ的特例——优先级取决于元素的插入次序
steap和queap也是PQ的特例——插入和删除的位置受限

1.基本实现

在这里插入图片描述

1.1 接口规范

template <typename T> struct PQ{
	virtual void insert(T) =0;//按优先级插入词条 ,=0为纯虚函数
	virtual T getMax() =0;//取出优先级最高的词条 
	virtual T delMax() =0;//删除优先级最高的词条 
}; 

1.2 实现探索

(1)基于vector(效率低不可取)
在这里插入图片描述
(2)基于list(效率低不可取)
在这里插入图片描述
(3)基于BBST(过犹不及,功能太强大,浪费资源)
在这里插入图片描述

2.实现——完全二叉堆Complete Binary Heap)

完全二叉树
在这里插入图片描述

2.1 性质

2.1.1 结构性——血肉

在这里插入图片描述
实现: PQ_ComplHeap=PQ+vector

template <typename T> class PQ_ComplHeap:public PQ<T>,public vector<T>{
	protected:
		Rank percolateDown(Rank n,Rank i);//下滤
		Rank percolateUp(Rank i);//上滤
		void heapity(Rank n);//Floyd建堆算法 
	public:
		PQ_ComplHeap(T* A,Rank n)//批量构造
		{copyFrom(A,0,n);heapify(n);}
		void insert(T);
		T getMax(){return this->_elem[0];}
		T delMax();
}; 

2.1.2 堆序性(堆顶即为最大元)——灵魂

在这里插入图片描述

2.2 操作

2.2.1 插入(结构性保持,堆序性却不一定)与上滤(比较一次)

插入)直接在向量末尾插入即可,此时结构性位置,堆序性遭到了破坏

template <typename T> void PQ_ComplHeap<T>::insert(T e){
	vector<T>::insert(e);
	percolateUp(this->_size-1);
}

上滤)若插入的比其逻辑意义上的父亲大,则互换位置,如此往复

template <typename T>
Rank PQ_ComplHeap<T>::percolateUp(Rank i){
	while(ParentValid(i)){//只要有父亲(尚未抵达堆顶),则 
		Rank j=Parent(i);//将i之父记作j 
		if(It(this->_elem[i],this->_elem[j])) break;//一旦父子不再逆序,上滤即完成 
		swap(this->_elem[i],this->_elem[j]);//否则,交换父子位置,并上升一层 (O(3logn)
		i=j;
	} 
	return i;
}

改进:直接将插入词条备份,下移父节点后,再将备份的词条放入合适的位置(logn+2)

2.2.2 删除(堆序性保持,结构性却不一定)与下滤(比较两次)

删除(logn)物理摘除最大元素后,将末元素与首元素交换,此时结构性恢复,但是堆序性遭到了破坏

template <typename T> T PQ_ComplHeap<T>::delMax(){
	T maxElem=this->_elem[0];
	this->_elem[0]=this->_elem[--this->_size];//摘除堆顶,用末元素取代
	percolateDown(this->_size,0);//对新堆顶实施下滤
	return maxElem;//返回此前备份的最大词条 
}

下滤与更大的孩子节点交换位置

template <typename T>
Rank PQ_ComplHeap<T>::percolateDown(Rank n,Rank i){
	Rank j;//i及其孩子中,能作为父亲的秩 
	while(i!=(j=properParent(this->_elem,n,i))){//只要i不是j,则 
		swap(this->_elem[i],this->_elem[j]);//换位 (O(3logn)
		i=j;//并继续考察i
	} 
	return i;
}

同样可以做优化

2.2.3 批量建堆

(1)自上而下的上滤(O(nlogn):来源于对所有节点深度的求和)

template <typename T> void PQ_ComplHeap<T>::heapify(Rank n){//蛮力 
	for(int i=1;i<n;i++)
		percolateUp(i); 
}

(2)自下而上的下滤(O(n):来源于对所有节点高度的求和)

template <typename T> void PQ_ComplHeap<T>::heapify(Rank n){//蛮力 
	for(int i=LastInternal(n);i<n;i--)//i=[n/2](取下)-1 
		percolateDown(n,i); //只需要考虑内部节点
}

2.3 应用

2.3.1 堆排序

template <typename T> void PQ_ComplHeap<T>::heapsort(Rank lo,Rank hi){
	PQ_ComplHeap<T> H(this->_elem+lo,hi-lo);//待排序区间建堆,O(n)
	while(!H.empty())//反复摘除最大元素并归入已排序后缀,直至堆空
		this->_elem[--hi]=H.delMax();//等效于堆顶与末元素兑换后下滤 
}

3.左式堆(堆合并)——满足堆序性,不满足非本质的结构性

堆合并方法探索
(1)A.insert(B.delMax())——O(m*log(n+m)
(2)union(A+B).heapify(n+m)——O(m+n)
(3)左式堆——O(logn)
1.条件:节点分布偏左,合并操作偏右
2.引入空节点长度(npl)
在这里插入图片描述
3.左倾性&左式堆
在这里插入图片描述
4.右侧链
在这里插入图片描述
4.模板类

template <typename T> class PQ_LeftHeap:public PQ<T>,public BinTree<T>{
	public:
		void insert(T);
		T getMax(){
			return this->root->data;
		} 
		T delMax();
};

5.算法实现

合并

template <typename T>
static BinNodePosi(T) merge(BinNodePosi(T) a,BinNodePosi(T) b){
	if(!a) return b;//递归基,a为空,返回b 
	if(!b) return a;//递归基,b为空,返回a 
	if(It(a->data,b->data)) swap(b,a);//此时ab非空,一般情况:首先确保b不大
	a->rc=merge(a->rc,b);//a的右子堆与b合并 
	a->rc->parent=a;//更新父子关系 
	if(!a->rc||a->lc>npl<a->rc->npl)//若有必要 
		swap(a->lc,a->rc);//交换a的左右子堆,以确保右子堆的npl不大 
	a->npl=a->rc?a->rc>npl+1:1;//更新a的npl 
	return a;//返回堆顶 

插入

template <typename T> void PQ_LeftHeap<T>::insert(T e){
	BinNodePosi(T) v=new BinNode<T>(e);
	this->_root=merge(this->_root,v);
	this->_root->parent=0;
	this->_size++;
}

删除

template <typename T> T PQ_LeftHeap<T>::delMax(){
	BinNodePosi(T) lHeap=this->_root->lc;
	BinNodePosi(T) rHeap=this->_root->rc;
	T e=this->_root->data;//备份堆顶处的最大元素
	delete this->_root;
	this->_size--;
	this->_root=merge(lHeap,rHeap) ;
	if(this->_root) this->_root->parent=0;
	return e;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值