Skip to content

skew heap

简介

就是左偏树改装一下,不再判断左子树高度比右子树高,而是每次插入就交换左右子树

与左偏树对比

code:

//leftist heap
int merge(int x,int y){
    if(!x || !y) return x+y;
    if(leq(y,x)) swap(x,y);
    rc[x]=merge(rc[x],y);
    if(dis[lc[x]]<dis[rc[x]]) swap(lc[x],rc[x]);
    dis[x]=dis[rc[x]]+1;
    return x;   
} 
//skew heap
int merge(int x,int y){
    if(!x || !y) return x+y;
    if(leq(y,x)) swap(x,y);
    rc[x]=merge(rc[x],y);
    swap(lc[x],rc[x]);
    return x;
}

看起来很像AVL转splay的操作?那么来分析下复杂度吧

复杂度证明

势能分析

\(\phi(i)\)表示\(i\)个节点的树中,重节点的个数

重节点为右子树大小大于左子树的节点,轻节点相反

假设合并的两个树\(a\),\(b\)右路径的重节点,轻节点个数为\(l_a,l_b,h_a,h_b\)

那么每次操作实际开销为\(c_i=l_a+l_b+h_a+h_b\)

因为每次走右路径,最多只会经过这些节点

则均摊开销\(\hat c_i=c_i+\Delta \phi(i)\)

因为每个节点都会交换左右子树,且重节点的合并发生在右子树,所以重节点一定变为轻节点(轻节点不一定变成重节点)。所以势能函数满足\(\Delta \phi (i)<l_a+l_b-h_a-h_b\)

而轻节点个数满足\(l_a,l_b<O(\log(N))\)

则有\(\hat c_i<l_a+l_b+h_a+h_b+l_a+l_b-h_a-h_b=2(l_a+l_b)=O(\log(N))\)

\(\sum c_i=\sum \hat c_i+\phi(0)-\phi(n)<N+N\log N\)

实际表现

在P3377中只比左偏树慢了40ms,表现看起来不错

~~总感觉skew heap是写leftist heap忘了写高度判断结果无意发现的数据结构~~

~~大家写代码时多多出错,说不定你就是下一个数据结构发明者~~

  • 注:在删除节点时注意写法: cpp fa[x]=fa[lc[x]]=fa[rc[x]]=merge(lc[x],rc[x]); //注意fa[x]有必要改,因为并查集有可能会经过x,尽管他已经被打上了标记(并查集本身难以删掉某个节点)