其他分享
首页 > 其他分享> > 平衡树

平衡树

作者:互联网

本人现已经学过,打过 没背过 的平衡树有 :

1.有旋treap
2.无旋treap
3.splay

现在想总结一下各平衡树

一,有旋treap

是oier常用且较短较好理解的平衡树, 重点的操作是左旋和右旋
思想大概是通过随机数维护一个BST, 并通过左旋和右旋使其保持平衡

上代码:

点击查看代码
#define combo void
#define eafoo int


#define ls(x) (tree[x].cl[0])
#define rs(x) (tree[x].cl[1])
#define val(x) (tree[x].val)
#define cnt(x) (tree[x].cnt)
#define data(x) (tree[x].data)
#define size(x) (tree[x].size)
struct Balance_tree
{
	struct treap
    {
		int cl[2], val, data, cnt, size;
	}tree[maxn];
	int root, tot;
    Treap()
    {
		srand((unsigned) time(NULL));
		newPoint(-INFFF), newPoint(INFFF);
		root = 1, rs(1) = 2;
		pushup(root);
		return;
	}
	combo pushup(int rt)
    {
		return (combo)(size(rt) = size(ls(rt)) + size(rs(rt)) + cnt(rt));
	}
	eafoo newPoint(int val)
    {
		val(++ tot) = val, data(tot) = rand(), cnt(tot) = size(tot) = 1;
		return tot;
	}
	combo zrg(int& rt)
    {//右旋 zig
		int v = ls(rt);
		ls(rt) = rs(v), rs(v) = rt, rt = v;
	 	return (combo)(pushup(rs(rt)), pushup(rt));
	} 
	combo zlg(int& rt)
    {//左旋 zag
		int v = rs(rt);
		rs(rt) = ls(v), ls(v) = rt, rt = v;
		return (combo)(pushup(ls(rt)), pushup(rt));
	}
	eafoo GetRankByVal(int rt, int val)
    {
		if(rt == 0)return 0;
		if(val == val(rt))return size(ls(rt)) + 1;
		if(val <  val(rt))return GetRankByVal(ls(rt), val);
		return GetRankByVal(rs(rt), val) + size(ls(rt)) + cnt(rt);
	}
	eafoo GetValByRank(int rt, int rank)
    {
		if(rt == 0)return INFFF;
        if(size(ls(rt)) >= rank)return GetValByRank(ls(rt), rank);
        if(size(ls(rt)) + cnt(rt) >= rank)return val(rt);
        return GetValByRank(rs(rt), rank - size(ls(rt)) - cnt(rt));
	}
	combo insert(int& rt, int val)
    {
        if(rt == 0)
        { 
            rt = newPoint(val);
            return;
        }
        if(val == val(rt))
        { 
            cnt(rt) ++, pushup(rt);
            return;
        }
        if(val < val(rt))
        {
            insert(ls(rt),val);
            if(data(rt) < data(ls(rt)))
                zrg(rt);
        }
        else
        {
            insert(rs(rt),val);
            if(data(rt) < data(rs(rt)))
                zlg(rt);
        }
        pushup(rt);
		return;
	}
	eafoo getPre(int val)
    {
		int ans = 1, rt = root;
        while(rt)
        {
            if(val == val(rt))
            {
                if(ls(rt) > 0)
                {
                    rt = ls(rt);
                    while(rs(rt) > 0)
                        rt = rs(rt);
                    ans = rt;
                }
                break;
            }
            if(val(rt) < val && val(rt) > val(ans))ans = rt;
            rt = val < val(rt) ? ls(rt) : rs(rt);
        }
        return val(ans);
	}
    eafoo getNext(int val)
    {
        int ans = 2, rt = root;
        while(rt)
        {
            if(val == val(rt))
            {
                if(rs(rt) > 0)
                {
                    rt = rs(rt);
                    while(ls(rt) > 0)
                        rt = ls(rt);
                    ans = rt;
                }
                break;
            }
            if(val(rt) > val && val(rt) < val(ans))ans = rt;
            rt = val < val(rt) ? ls(rt) : rs(rt);
        }
        return val(ans);
    }
    inline combo deletee(int& rt, int val)
    {
        if(rt == 0)return;
        if(val == val(rt))
        {
            if(cnt(rt) > 1)
            {
                cnt(rt)--, pushup(rt);
                return;
            }
            if(ls(rt) || rs(rt))
            {
                if(rs(rt) == 0 || data(ls(rt)) > data(rs(rt)))
                     zrg(rt), deletee(rs(rt), val);
                else zlg(rt), deletee(ls(rt), val);
                pushup(rt);
            }
            else rt = 0;
            return;
        }
        val < val(rt) ? deletee(ls(rt), val) : deletee(rs(rt), val);
        pushup(rt);
        return;
    }
}T;

二,无旋treap (fhq_treap)

是范浩强大佬发明的是最短的平衡树, 思想也比较简单, 考场上常用 虽然我怀疑我考场上想不想得起来打平衡树

主要思想是分裂和和并, 也用上了treap的随机数。

上代码

点击查看代码
#define combo void
#define eafoo int

#define ls(x) (tree[x].cl[0])
#define rs(x) (tree[x].cl[1])
#define sz(x) (tree[x].size)
#define val(x) (tree[x].val)
#define dat(x) (tree[x].dat)

struct Balance_tree
{
    struct fhq_treap
    {
        int cl[2], size, val, dat;
    }tree[maxn];
    int root, tot;
    queue <int> del_list;

    Balance_tree()
    {
        srand((unsigned)time(NULL));
        newNode(-INFFF), newNode(INFFF);
        root = 1, rs(root) = 2;
        return;
    }

    eafoo newNode(int val)
    {
        int tmp; 
        if(del_list.empty())
        {
            tmp = ++tot;
        }
        else
        {
            tmp = del_list.front(), del_list.pop();
        }
        dat(tmp) = rand(), val(tmp) = val, sz(tmp) = 1;
        return tmp;
    }

    combo pushup(int rt)
    {
        return (void)(sz(rt) = sz(ls(rt)) + sz(rs(rt)) + 1);
    }

    combo insert(int val)//加入
    {
        int x, y;
        split_val(root, val, x, y);// 先按val分裂成两棵子树,则x子树皆<=val;
        root = merge(merge(x, newNode(val)), y);//让val与x合并,再与y合并;
        return;
    }

    combo split_val(int pos, int val, int& x, int& y)//按权值分裂
    {
        //在pos点时,以val分裂成x,y两棵树
        if(!pos)
        {
            x = y = 0;
            return;
        }
        if(val(pos) <= val)
        {
            x = pos, split_val(rs(pos), val, rs(pos), y);   
        }
        else
        {
            y = pos, split_val(ls(pos), val, x, ls(pos));
        }
        pushup(pos);
        return;
    }

    combo split_size(int pos, int siz, int& x, int& y)//按size分裂
    {
        //在pos点时,以siz分裂成x,y两棵树
        if(!pos)
        {
            x = y = 0;
            return;
        }
        if(siz > sz(ls(pos)))
        {
            x = pos, split_size(rs(pos), siz - sz(ls(pos)) - 1, rs(pos), y);
        }
        else
        {
            y = pos, split_size(ls(pos), siz, x, ls(pos));
        }
        pushup(pos);
        return;
    }

    eafoo merge(int x, int y)//合并
    {
        if(!x || !y) return x + y;
        if(dat(x) > dat(y))
        {
            rs(x) = merge(rs(x), y);
            pushup(x);
            return x;
        }
        ls(y) = merge(x, ls(y));
        pushup(y);
        return y;
    }

    combo deletee(int val)//删除
    {
        //先分裂出<=val的,再在x中按val-1分裂,则yy为与val相等的所有点
        int x, y, z;
        split_val(root, val, x, z),
        split_val(x, val - 1, x, y);
       // del_list.push(y);
        y = merge(ls(y), rs(y));
        root = merge(merge(x, y), z);
        return;
    }
    
    eafoo getValbyRank(int rank)
    {
        ++rank;
        int x, y, cur;
        split_size(root, rank, x, y);
        cur = x;
        while(rs(cur))
        {
            cur = rs(cur);
        }
        root = merge(x, y);
        return val(cur);
    }

    eafoo getRankbyVal(int val)
    {
        int x, y, ans;
        split_val(root, val - 1, x, y);
        ans = sz(x); 
        root = merge(x, y);
        return ans;
    }

    eafoo getPre(int val)
    {
        int x, y, cur;
        split_val(root, val - 1, x, y);
        cur = x;
        while(rs(cur))
        {
            cur = rs(cur);
        }
        root = merge(x, y);
        return val(cur);
    }

    eafoo getNext(int val)
    {
        int x, y, cur;
        split_val(root, val, x, y);
        cur = y;
        while(ls(cur))
        {
            cur = ls(cur);
        }
        root = merge(x, y);
        return val(cur);
    }
}T;

三,splay

是一个很常用的平衡树,虽然代码比较长,但很稳定,且可以用来处理区间翻转问题

重要思想是splay: 把当前点认为为最常用的点并旋为根节点

点击查看代码
#define combo void 
#define eafoo int
#define ekang bool 

#define ls(x) (S_tree[x].cl[0])
#define rs(x) (S_tree[x].cl[1])
#define fa(x) (S_tree[x].fa)
#define sz(x) (S_tree[x].size)
#define val(x) (S_tree[x].val)
#define cnt(x) (S_tree[x].cnt)
#define lazy(x) (S_tree[x].lazy)
#define son(x,y) (S_tree[x].cl[y])
struct Balance_Tree
{
    struct splay
    {
        int cl[2], val, fa, size, cnt, lazy;
    }S_tree[maxn];
    int root, tot;
    inline combo newPoint(int val, int fa, int nxt)
    {
        return (combo)(fa(++ tot) = fa, val(tot) = val, sz(tot) = cnt(tot) = 1, son(fa, nxt) = tot);
    }
    inline combo pushup(int rt)
    {
        return (combo)(sz(rt) = sz(ls(rt)) + sz(rs(rt)) + cnt(rt));
    }
    inline ekang cl_fa(int rt)
    {
        return rs(fa(rt)) == rt;
    }
    inline combo connect(int rt, int y, int nxt)
    {
        return (combo)(son(y,nxt) = rt, fa(rt) = y);
    }
    inline combo rotato(int rt)
    {
        int fa = fa(rt), ffa = fa(fa),
            fl1 = cl_fa(rt), fl2 = cl_fa(fa);
        connect(son(rt, fl1 ^ 1), fa, fl1),
        connect(fa, rt, fl1 ^ 1),
        connect(rt, ffa, fl2);
        pushup(fa), pushup(rt);
        return;
    }
    inline combo splay(int x, int y)
    {
        y = fa(y);
        while(fa(x) != y)
        {
            if(fa(fa(x)) == y)
                 rotato(x);
            else if(cl_fa(x) == cl_fa(fa(x)))
                 rotato(fa(x)), rotato(x);
            else rotato(x), rotato(x);
        }
        if(y == 0)
        {
            root = x;
            connect(x, 0, 1);
        }
        return;
    }
    inline combo updata(int val)
    {
        if(root == 0){
            newPoint(val, 0, 1);
            root = tot;
            return;
        }
        int now = root;
        while(1)
        {
            sz(now) ++;
            if(val(now) == val)
            {
                cnt(now) ++, splay(now, root);
                return;
            }
            int nxt = val(now) < val, son = son(now, nxt);
            if(!son)
            {
                newPoint(val, now, nxt), splay(tot, root);
                return;
            }
            now = son;
        }
        return;
    }
    inline eafoo find(int val)
    {
        int now = root;
        while(1)
        {
            if(! now)
                return 0;
            if(val(now) == val)
            {
                splay(now, root);
                return now;
            }
            int nxt = val(now) < val, son = son(now, nxt);
            now = son;
        }
    }
    inline combo deletee(int val)
    {
        int now = find(val);
        if(! now)
            return;
        if(cnt(now) > 1)
            return (combo)(cnt(now) --, sz(now) --);
        if(! ls(now) && ! rs(now))
            return (combo)(root = 0);
        if(!ls(now))
            return (combo)(root = rs(root), fa(root) = 0);
        if(!rs(now))
            return (combo)(root = ls(root), fa(root) = 0);
        else {
            int pos = ls(now);
            while(rs(pos))
                pos = rs(pos);
            splay(pos, root);
            connect(rs(now), pos, 1);
            pushup(pos);
            return;
        }
    }
    inline int GetRankByVal(int val)
    {
        int now = root, s = 0;
        while(now)
        {
            if(val(now) == val)
            {
                splay(now, root);
                return sz(ls(now)) + 1;
            }
            if(val(now) < val)
                 s += sz(ls(now)) + cnt(now), now = rs(now);
            else now = ls(now);
        }
        return s + 1;
    }
    inline int GetValByRank(int k)
    {
        int now = root;
        while(1)
        {
            int used = sz(now) - sz(rs(now));
            if(k > sz(ls(now)) && k <= used)
                break;
            if(k > used)
                k -= used, now = rs(now);
            else now = ls(now);
        }
        splay(now, root);
        return val(now);
    }
    inline int getPre(int val)
    {
        int ans = - INFFF, now = root;
        while(now)
        {
            if(val(now) < val && val(now) > ans)
                 ans = val(now);
            if(val > val(now))
                 now = rs(now);
            else if(val == val(now))
                return val;
            else now=ls(now);
        }
        return ans;
    }
    inline int getNext(int val)
    {
        int ans = INFFF, now = root;
        while(now)
        {
            if(val(now) > val && val(now) < ans)
                 ans = val(now);
            if(val < val(now))
                 now = ls(now);
            else if(val == val(now))   
                 return val;
            else now = rs(now);
        }
        return ans;
    }
}S;

维护区间时,不用其存点,以一个类似于线段树的方式维护区间端点

点击查看代码
#define combo void 
#define eafoo int
#define ekang bool 

#define ls(x) (S_tree[x].cl[0])
#define rs(x) (S_tree[x].cl[1])
#define fa(x) (S_tree[x].fa)
#define sz(x) (S_tree[x].size)
#define val(x) (S_tree[x].val)
#define cnt(x) (S_tree[x].cnt)
#define lazy(x) (S_tree[x].lazy)
#define son(x,y) (S_tree[x].cl[y])
struct Balance_tree
{
    struct splay
    {
        int cl[2], val, fa, size, cnt, lazy;
    }S_tree[maxn];
    int root, tot;
    inline combo pushup(int rt)
    {
        return (combo)(sz(rt) = sz(ls(rt)) + sz(rs(rt)) + 1);
    }
    inline combo pushdown(int rt)
    {
        if(lazy(rt))
        {
            int& lc = ls(rt), & rc = rs(rt);
            swap(lc, rc);
            lazy(lc) ^= 1, lazy(rc) ^= 1, lazy(rt) = 0;
        }
        return;
    }
    inline combo build(int& rt, int l, int r)
    {
        if(l > r)return ;
        int mid = (l + r) >> 1;
        if(mid < rt)
             ls(rt) = mid;
        else rs(rt) = mid;
        fa(mid) = rt, sz(mid) = 1;
        if(l == r)return;
        build(mid, l, mid - 1), build(mid, mid + 1, r);
        pushup(mid);
    }
    inline combo rotate(int rt, int& k){//将x旋转至他爹那儿
        int y = fa(rt), z = fa(y),
            fl = rs(y) == rt;
        if(y == k)
             k = rt;
        else son(z,rs(z) == y) = rt;
        fa(son(rt, fl ^ 1)) = y, fa(y) = rt,fa(rt) = z,
        son(y, fl) = son(rt, fl ^ 1), son(rt, fl ^ 1) = y;
        pushup(rt), pushup(y);
        return;
    }
    inline combo splay(int rt, int& k)
    {//将rt旋转到k那儿
        while(rt != k)
        {
            int y = fa(rt), z = fa(y);
            if(y != k)
            {
                if((ls(z) == y) ^ (ls(y) == rt))
                    rotate(rt, k);
                else rotate(y, k);
            }
            rotate(rt, k);
        }
        return;
    }
    inline eafoo select(int& rt, int k)
    {
        pushdown(rt);
        //cerr<<rt<<endl;
        int sum = sz(ls(rt)) + 1;
        if(sum == k)return rt;
        if(sum > k)
             return select(ls(rt), k);
        else return select(rs(rt), k - sum);
    }
    inline combo rever(int l, int r)
    {
        splay(l, root), splay(r, rs(l));
        lazy(ls(r)) ^= 1;
    }
    inline combo print(int rt)
    {
        pushdown(rt);
        if(ls(rt)) print(ls(rt));
        if(2 <= rt && rt <= n + 1)
            printf("%lld ", rt - 1);
        if(rs(rt)) print(rs(rt));
        return; 
    }
}S;

标签:rt,return,val,int,ls,平衡,now
来源: https://www.cnblogs.com/liyikang/p/16428721.html