zoukankan      html  css  js  c++  java
  • 【可并堆】【数据结构】左偏树简介

    左偏树

    Noip大概率翻皮水了,然后先继续xjb学习吧,顺便文化课也是翻皮水大队的:(

    今天介绍一种特殊的数据结构:可并堆中的一种->左偏树(好吧其实是因为这种简单易懂代码复杂度较低).

    基本介绍

    左偏树,故名思义,它是颗向左倾斜的树,其实,它还是棵二叉树,再者,它还具有堆的性质,but,它不是堆.

    那么显然,左偏树看起来就像是优化堆一些难以用较优复杂度实现的操作,其实主要就是一个操作:合并.

    我们知道,传统的二叉堆,是需要暴力合并的,复杂度为(O(sz1+sz2)),而本文所涉及的左偏树,复杂度为$O(log_{2} sz1sz2) $.

    首先介绍一个定义:一个节点到其子树内最近叶节点的距离称之为这个节点的高度Height,简记为ht.

    接下来给出一些左偏树的性质.

    基本性质

    性质1

    堆的性质:对于任意节点P,(val_{P})<(或>)(val_{lson[p]})(val_{rson[p]})

    性质2

    [ht_{lson[P]} geq ht_{rson[P]} ]

    左偏树顾名思义,向左倾斜的树,就是这个性质在图像可视化后的诠释.

    性质3

    [ht_{P}=ht_{lson[P]}+1 ]

    很好理解,由性质2以及叶节点ht为0可以得出.

    性质4

    对于一棵(n)节点的左偏树,(max{ht} leq log_{2}(n+1)-1)

    给出证明如下,设(max{ht}=k)

    那么,显然节点数最少的情况为一棵满二叉树,此时,节点数为(2^{k+1}-1)

    (n geq 2^{k+1}-1),由数学推导可得性质4.

    这个性质主要保证了左偏树的复杂度.

    接下来简单介绍一下一些基本操作.

    基本操作

    1.合并

    这是左偏树最基础也是最重要的操作,这里介绍小根堆的情况:

    首先,令根节点权值较小的为x,否则为y,记根节点分别为X、Y.

    首先在根节点的右子树最右链中找到第一个比Y大的位置,将Y作为其父亲,然后不断递归调用合并Y的右子树和以该节点为根的右子树即可,并维护更新相关信息.

    然后发现更新后,右子树的ht可能比左子树大,此时交换两个子树即可.

    其实上述合并过程的实现本质是一样的,下面给出参考,对于复杂度的分析,不多给出解释.

    inline int merge(int x,int y){
    	if (!x||!y) return x+y;
    	if (v[x]>v[y]||(v[x]==v[y]&&x>y)) swap(x,y);
    	ch[x][1]=merge(ch[x][1],y);
    	f[ch[x][1]]=x;
    	if (ht[ch[x][0]]<ht[ch[x][1]]) swap(ch[x][0],ch[x][1]);
    	ht[x]=ht[ch[x][1]]+1;return x;
    }
    

    2.插入

    本质上就是将一棵只有一个节点的左偏树与当前树合并.

    3.查询最大/最小值

    直接返回根结点的权值.

    4.删除根节点

    合并根节点两棵子树即可.

    5.构造一棵新左偏树

    法1:

    暴力插入

    法2:

    分治思想,分段处理,直至剩余1个节点,然后返回并merge,复杂度约为O(n).

    6.删除任意节点

    子树内维护与删除根节点一样,然后向上传递信息并更改,维护性质即可.

    模板

    给出模板题的模板

    见下:

    #include <stdio.h>
    #define MN 100005
    #define R register
    inline int read(){
    	R int x; R bool f; R char c;
    	for (f=0; (c=getchar())<'0'||c>'9'; f=c=='-');
    	for (x=c-'0'; (c=getchar())>='0'&&c<='9'; x=(x<<3)+(x<<1)+c-'0');
    	return f?-x:x;
    }
    inline int max(int a,int b){return a>b?a:b;}
    inline int min(int a,int b){return a<b?a:b;}
    inline void swap(int &a,int &b){a^=b,b^=a,a^=b;}
    int n,q,ch[MN][2],v[MN],ht[MN],f[MN];
    inline int gf(int u){
    	while(f[u]) u=f[u];
    	return u;
    }
    inline int merge(int x,int y){
    	if (!x||!y) return x+y;
    	if (v[x]>v[y]||(v[x]==v[y]&&x>y)) swap(x,y);
    	ch[x][1]=merge(ch[x][1],y);
    	f[ch[x][1]]=x;
    	if (ht[ch[x][0]]<ht[ch[x][1]]) swap(ch[x][0],ch[x][1]);
    	ht[x]=ht[ch[x][1]]+1;return x;
    }
    inline void pop(int x){
    	v[x]=-1;f[ch[x][0]]=f[ch[x][1]]=0;
    	merge(ch[x][0],ch[x][1]);
    }
    int main(){
    	n=read(),q=read();ht[0]=-1;
    	for (R int i=1; i<=n; ++i) v[i]=read();
    	for (R int i=1; i<=q; ++i)
    		if (read()==1){
    			R int x=read(),y=read();
    			if (!(~v[x])||!(~v[y])) continue;
    			x=gf(x),y=gf(y);if (x==y) continue;
    			merge(x,y);
    		}
    		else{
    			R int x=read();
    			if (!(~v[x])) puts("-1");
    			else {x=gf(x);printf("%d
    ",v[x]);pop(x);}
    		}
    	return 0;
    }
    
  • 相关阅读:
    React.memo()使用教程
    React组件什么时候render
    redux-thunk使用教程
    Redux数据持久化
    Mac 中安装 nvm: 切换Node 不同版本
    上传文件类型选择
    Python获取软件安装列表
    Java读取text文件
    负载均衡
    远程调用
  • 原文地址:https://www.cnblogs.com/Melacau/p/leftist_tree.html
Copyright © 2011-2022 走看看