左偏树模板讲解

左偏树

左偏树是可合并的二叉堆,首先满足所有的堆的性质,其外,它还可以合并。

左偏树的树节点需要保存的信息有:
       1.左右子树节点编号
       2.此节点到有空子结点(子节点数不足2个的节点)的结点的最短距离dist
       3.自身权值
性质

左偏树除了堆的所有性质,它还要满足的重要的性质就是“左偏”。

左偏
这个性质保证了它的操作都是O(logn)的。
左偏就是每个节点的左子节点的dist不小于右子节点的dist
(但并不代表左子节点数一定不小于右子节点数),
那么可知dist[i] =dist[rc[i]]+1;

如图(圈内是节点权值,蓝字就是dist值。)

操作

堆可以做到的是:插入(O(logn)),查询最值(O(1)),删除堆顶(O(logn));
对于左偏树,这些操作都是基于合并的(除了查询最值),而且复杂度都仍然是O(logn)。
左偏树合并操作合并的是两棵左偏树,对于堆的插入,就是合并一棵树和一个节点,对于堆的删除,就是合并根的两棵子树。
合并过程

以小根堆为例,
合并A, B两个堆
    如果 A < B(这个不满足的话swap(a,b))and两棵树的节点没有包含关系(就是没有相同的节点)。
    比较B和A的右子树大小,
    如果B  <  A的右子树,那么swap B和A的右子树,
    接着将B看成刚刚的A,继续swap;
    如果B>A的右子树,那么继续找这颗树的右子树。
    而这样可能会破坏左偏的性质,
    所以需要在回溯的过程中维护左偏性质,
    通过交换左右子树完成。
总的来说,左偏树的核心操作,合并(merge),是在右子树上进行的,
又因为要保证每个节点的左子节点的dist不小于右子节点的dist,
而且有dist[i] =dist[rc[i]]+1,
所以一棵左偏树的效率是O(logn)的

下面附上图会理解得更清晰:




// 左偏树模板, 以大根堆为例
struct node{
    int w, lc, rc, h;
}t[M];//结构体
//核心操作 :合并(函数返回值:树根)
int merge(int A, int B){
    if(!A||!B) return A+B;
    if(t[A].w < t[B].w) swap(A, B);
    t[A].rc = merge(t[A].rc, B);
    maintain(A); //维护A的一些信息(此模板结构体中并没有),如:子节点数
    if(t[t[A].lc].h < t[t[A].rc].h) swap(t[A].lc, t[A].rc);
    if(t[A].rc) t[A].h = t[A].rc+1;
    else t[A].h = 0;
    return A;
} 
//插入 
void insert(int A, int x){
    cnt++;
    t[cnt].w = x; 
    merge(A, cnt);
} 
//删除
void del(int A){
    merge(t[A].lc, t[A].rc);
} 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值