堆与左偏树/堆

堆(heap):满足某结点,不大于或者小于其父节点的值。

一般来说,常见的堆被默认为 二叉堆

**堆的常见操作 **: STL : priority_queue p; //stl中的优先队列就是用的堆排

\(1)取得堆顶的元素\)

\(2)移除堆顶元素\)

\(3)插入堆中\)

假设以 p<<1 , p<<1|1 完全二叉树下标的性质来建堆, 那么堆顶即为 \(heap[1]\)

在堆中插入元素时,可以先插入到堆尾,然后再向父节点比较进行调整,调节到根结点结束或者符合条件时结束

再移除堆顶对堆进行调整时,实际上就是一个 插入调整的逆向过程 , 将堆尾放置堆顶,然后向下调节

Code : 以下是我写的一个小根堆 . 例题 P3378 【模板】堆

struct Heap{
	int heap[maxn];//结点	
	int size = 0;//大小
	/*
		push
		1.插入堆尾
		2.从堆尾开始调整堆
		3.根据大/小顶堆的方式调整
	*/
	void push(int x){
		heap[++size] = x;
		int p = size,q;
		while(p > 1){
			q = p >> 1;
			if(heap[p] >= heap[q]) return ;
			else swap(heap[p] , heap[q]);
			p = q;
		}
	}
	int top(){
		return heap[1];//堆顶
	}
	/*
		1.将堆尾的数值交给堆顶
		2.从堆顶开始向下调整
	*/
	void pop(){
		int p = 1,q;
		heap[1] = heap[size--];
		while(p*2 <= size){
			q = p << 1;
			if( q<size && heap[q+1] < heap[q] ) q++; //如果右子点还存在更小的则比较右子点
			if(heap[p] <= heap[q]) return;
			swap(heap[p],heap[q]);
			p = q;
		}
	}
}hp;

我们明确了堆的操作之后,现在又遇到一个新的问题。如果要将 多个堆进行合并应该怎么操作呢?

把所有的堆重新 放在一个新堆中,那不是\(O(nlog^n)\) ,这时候要降低合并的复杂度,就需要一种可并数据结构 : 左偏树 (leftist heap/tree)

由于左偏树不是一个完全二叉树,所以要单独记录 左节点与右节点 下标信息

\(1.\)左偏树具有 堆性质 ,即若其满足小根堆的性质,则对于每个结点 \(x\) ,有 $v_x\le v_{lc},v_x\le v_{rc} $

\(2.\)左偏树具有 左偏性质 ,即对于每个结点 \(x\) ,有 $dist_{lc}\ge dist_{rc} $

\(3.\) 对于结点 \(x\) , \(dist_x = dist_{rc}+1\)

操作: \(以下操作默认为小根堆\)

\(1.\)merge 合并

我们假设 \(X\) 的根节点小于等于 \(Y\) 的根节点(否则交换\(X,Y\)),把 \(X\) 的根节点作为新树 \(Z\) 的根节点,剩下的事就是合并 \(X\) 的右子树和 \(Y\)

合并了 \(X\) 的右子树和 \(Y\) 之后,\(X\) 的右子树的距离可能会变大,当 \(X\) 的右子树 的距离大于 \(X\) 的左子树的距离时,性质二会被破坏。在这种情况下,我们只须要交换 \(X\) 的右子树和左子树。

而且因为 \(X\) 的右子树的距离可能会变,所以要更新\(dist_x = dist_{rc}+1\) 这样就合并完了

Code 例题 P3377 【模板】左偏树(可并堆)

#include <cstdio>
#include <iostream>
const int maxn = 1e5+5;
using namespace std ;
#define ls s[x].lc
#define rs s[x].rc
struct leftist_tree{
	int dis, val, lc, rc, rt ;
}s[maxn] ;
//路径压缩 
int find(int x){
	return s[x].rt == x?x:s[x].rt=find(s[x].rt);//对父结点路径压缩
}
int merge(int x,int y){
	if(!x || !y) return x+y; 
	if(s[x].val > s[y].val || (s[x].val == s[y].val && x > y)) swap(x,y);
	rs = merge(rs,y);
	if(s[ls].dis < s[rs].dis) swap(ls,rs);
	s[ls].rt = s[rs].rt = s[x].rt = x;
	s[x].dis = s[rs].dis + 1;
	return x;
}
void pop(int x){
	s[x].val = -1; 
	s[ls].rt =  ls;
	s[ls].rt =  ls;
	s[x].rt = merge(ls,rs);
}

int main(){
	int n,m;
	cin>>n>>m;
	s[0].dis = -1;//根的距离定为-1
	for(int i=1;i<=n;i++){
		s[i].rt = i, cin>>s[i].val;
	}
	for(int i=1;i<=m;i++){
		int op;
		cin>>op;
		if(op==1){
			int x,y;
			cin>>x>>y;
			if(s[x].val == -1 || s[y].val == -1) continue;//其中有不存在的部分
			int fx = find(x),fy = find(y);
			if(fx != fy) s[fx].rt = s[fy].rt = merge(fx,fy);
		}else{
			int x; 
			cin>>x;
			if(s[x].val == -1) cout<<-1<<endl;
			else {
				cout<<s[find(x)].val<<endl;
				pop(find(x));
			}
		}
	}
	return 0;
}
posted @ 2020-05-09 10:53  Tianwell  阅读(165)  评论(0编辑  收藏  举报