[算法学习笔记]左偏树的基本操作及其应用

简介

左偏树是一种可以快速支持合并等操作的堆, 是可并堆中代码复杂度最低,也最容易理解的一种(注意左偏树的每一棵子树都为左偏树)

性质

左偏树是一种二叉树, 除了有二叉树的左右儿子,还有2个属性,键和距离。下面是左偏树的一些基本性质。

  • 节点的键值小于或等于左右子节点的键值。这是左偏树的堆性质。

  • 节点的左子节点的距离不小于右子节点的距离。 这是左偏树的左偏性质。

  • 节点的距离等于它的右子节点距离加一。 若无则为0.

  • 若左偏树的距离为一定值,则节点数最少的左偏树是完全二叉树。(显然啊)

基本操作

数组定义


struct Point{
    int val,ls,rs,dis,fa;
    //val 为 权值
    //ls,rs 为左右子树
    //dis为距离
    //fa 为 father!
}tree[maxn];

Merge

​ 顾名思义, Merge操作就是把两个左偏树并起来, 注意并后的左偏树一定要满足上面的四条性质。现在我们令x, y为要合并的两个堆的根, 下面即是步骤。

  • 如果\(x\)\(y\) 为空(即等于0) 返回 x + y(就是\(x\)\(y\) 非零的那一个)
  • 为了保持一定是val大的的并到小的上面, 所以\(if(tree[x].val > tree[y].val) swap(x, y);\)(大根堆则反之)
  • 现在我们将\(y\)树并到\(x\)的右子树上.(因为\(x\)的val比\(y\)的小, 所以并到他的子树中, 又因为这棵树是左偏的,你就把它并到右子树中吧)
  • 为了维持上面的第二条性质, 若\(tree[tree[x].ls].dis < tree[tree[x].rs].dis\)\(swap(tree[x].ls, tree[x].rs)\)
  • 更新x的距离
int merge(int x, int y)
{
    if (x == 0 || y == 0)
       return x+y;
    if ((tree[x].val > tree[y].val) || (tree[x].val == tree[y].val && x > y)) swap(x,y);
    tree[x].rs = merge(tree[x].rs,y);
    tree[tree[x].rs].fa = x;
    if(tree[tree[x].ls].dis < tree[tree[x].ls].dis)
    	swap(tree[x].ls,tree[x].rs);
    tree[x].dis = tree[tree[x].rs].dis + 1;
    return x;
}

Insert

​ Insert操作为插入一个新的节点, 把这个节点看做一颗新的左偏树即可


	int New(int x, int y){
		tree[++tot].val = y;
		tree[tot].ls = tree[tot].rs = 0;
		return merge(x, y);
	}

Delete

​ 把根提出来, 合并左右节点就好了

	root[x]= merge(tree[x].ls, tree[x].rs);

Top

​ emmm, \(Top = tree[x].val\) (x 为 堆的根)

例题

模板

​ 以洛谷p3377为例, 因为此题需要维护联通性, 所以需要像并查集一样暴力找父亲(不能路径压缩)

​ 代码丑, 建议不看, 以后会更新的

#include <bits/stdc++.h>
using namespace std;
int n,m;
struct Point{
    int val,ls,rs,dis,fa;
}tree[1000010];
template<class T>
void read(T &a){
    T s = 0, w = 1;
    char c = getchar();
    while(c < '0' || c > '9'){
    	if(c == '-') w = -1;
    	c = getchar();
    } 
    while(c >= '0' && c <= '9'){
    	s = (s << 1) + (s << 3) + (c ^ 48);
    	c = getchar();
    }
    a = w*s;
}
int merge(int x, int y){
    if (x == 0 || y == 0)
       return x+y;
    if ((tree[x].val > tree[y].val) || (tree[x].val == tree[y].val && x > y)) swap(x,y);
    tree[x].rs = merge(tree[x].rs,y);
    tree[tree[x].ls].fa = x;
    if(tree[tree[x].ls].dis < tree[tree[x].rs].dis){
    	swap(tree[x].ls,tree[x].rs);
    }
    tree[x].dis = tree[tree[x].rs].dis + 1;
    return x;
}
int find(int x){
    if(tree[x].fa == 0) return x;
    return find(tree[x].fa);
}
int main(){
    read(n); read(m);
    tree[0].dis = -1;
    for (int i = 1; i <= n; i++){
    	int x;
    	cin>>x;
    	tree[i].val = x;
    }
    for (int i = 1; i <= m; i++){
    	int opx;
    	read(opx);
    	if(opx == 1){
    		int x,y;
    		read(x); read(y);
    		if(tree[x].val == -1 || tree[y].val == -1) continue;
    		int xx = find(x), yy = find(y);
    		if(xx == yy) continue;
    		merge(xx,yy); 
    	}
    	if(opx == 2){
    		int x;
    		read(x);
    		int xx = find(x);
    		printf("%d\n",tree[xx].val);
    		if(tree[xx].val == -1) continue;
    		tree[xx].val = -1;
            int ls = tree[xx].ls;, rs = tree[xx].rs;
    		tree[xx].ch[0] = tree[xx].ch[1] = 0;
    		tree[ls].fa = 0,tree[rs].fa = 0;
    		merge(ls,rs);
    	}
    }
    return 0;
}
posted @ 2019-03-25 20:22  Ender_zzm  阅读(244)  评论(0编辑  收藏  举报