zoukankan      html  css  js  c++  java
  • 【学习笔记】splay入门(更新中)

    声明:本博客所有随笔都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。

    前言

    终于学习了 spaly (splay) !听说了很久,因为dalao总是那这个开玩笑所以对它有深深的恐惧...但是学起来没有那么难啦,可能是因为提前学了替罪羊树?(学替罪羊树真的是痛苦555)

    模板

    P3369 【模板】普通平衡树

    固定变量:

    (edge) - 这个节点的值

    (tot) - 这个节点重复的数

    (son[0/1]) - 左儿子右儿子

    (fa) - 节点的父亲

    (size) - 子树的大小

    struct ndoe{
    	int tot, size, son[2], fa, edge;
    }tree[N]; 
    

    add(x)(添加一个点):

    1.找到特殊点

    2.判断 (x) 与特殊节点的值

    1)相等,则直接 (++ tot)

    2)不相等,则新建节点,更新变量

    void add(int x)	
    {
    	int u = root, fa = 0;
    	while(u && tree[u].edge != x)
    		fa = u, u = tree[u].son[x > tree[u].edge];
    	if(u) ++ tree[u].tot;
    	else
    	{
    		u = ++ num;
    		if(fa) tree[fa].son[x > tree[fa].edge] = u;
    		tree[u].edge = x, tree[u].size = tree[u].tot = 1, tree[u].fa = fa;
    	}
    	splay(u, 0);
    }
    

    del(x)(删除一个点):

    1.找到 (x) 的前驱后继

    2.把前驱 (splay) 至根节点,把后继 (splay) 成前驱的儿子

    3.此时后继的左儿子则为要删除的点,判断此节点的个数

    1)个数大于一则只用去掉一个,然后 (splay)

    2)个数为一则直接删掉

    void del(int x)
    {
    	int xpre = next(x, 0), xnxt = next(x, 1);
    	splay(xpre, 0), splay(xnxt, xpre);
    	int u = tree[xnxt].son[0];
    	if(tree[u].tot > 1) -- tree[u].tot, splay(u, 0);
    	else tree[xnxt].son[0] = 0;
    }
    

    splay(x, goal)(将 (x) 旋成 (goal) 的儿子):

    1.判断 (x) 是否是 (goal) 的儿子

    1)不是,则判断 (x) 和它的父亲、祖先是否在一条线上,进行不同的 (splay)

    2)是,判断 (x) 是否是根并更新

    void splay(int x, int goal)
    {
    	while(tree[x].fa != goal)
    	{
    		int y = tree[x].fa, z = tree[y].fa;
    		if(z != goal) ((tree[z].son[0] == y) ^ (tree[y].son[0] == x)) ? rotate(x) : rotate(y);
    		rotate(x);
    	} 
    	if(! goal) root = x;
    }
    

    rotate(x)(单旋):

    1.更新 (x)(z) 的父子关系

    2.更新 (y) 和 ( (x) 原来和 (y) 对应的那个儿子)的父子关系

    3.更新 (x)(y) 的父子关系

    4.(update x、y)

    void rotate(int x)
    {
    	int y = tree[x].fa, z = tree[y].fa, k = (tree[y].son[1] == x);
    	tree[z].son[tree[z].son[1] == y] = x, tree[x].fa = z;
    	tree[y].son[k] = tree[x].son[k ^ 1], tree[tree[x].son[k ^ 1]].fa = y;
    	tree[x].son[k ^ 1] = y, tree[y].fa = x;
    	update(y), update(x);
    }
    

    pre(x)(前驱):

    1.将 (x) 变为树根

    2.有左子树则进入左子树,没有则代表没有比它小的数了

    3.进入左子树后一路往右子树上凑(找最大的)

    nxt(x)(后继):

    1.将 (x) 变为树根

    2.有右子树则进入右子树,没有则代表没有比它大的数了

    3.进入右子树后一路往左子树上凑(找最小的)

    int next(int x, int f)//我把pre和nxt写到一起啦,实际上是一样的
    {
    	find(x);
    	int u = root;
    	if((tree[u].edge > x && f) || (tree[u].edge < x && (! f))) return u;
    	u = tree[u].son[f];
    	while(tree[u].son[f ^ 1]) u = tree[u].son[f ^ 1];
    	return u;
    }
    

    find(x)(辅助函数):

    1.找到值等于 (x) 的那个节点

    2.将节点旋转至树根

    void find(int x)
    {
    	int u = root;
    	if(! u) return;
    	while(tree[u].son[x > tree[u].edge] && tree[u].edge != x)
    		u = tree[u].son[x > tree[u].edge];
    	splay(u, 0);
    }
    

    query(x)(第k大的数):

    1.先判断整个树的 (size) 是否大于等于 (x),若没有则不存在

    2.分三种情况继续讨论:

    1)左子树 (size) 大于 (x) ,前往左子树

    2)左子树+这个节点的数的个数大于 (x),则直接返回这个节点的值

    3)更新 (x),前往右子树

    int query(int x)
    {
    	int u = root;
    	if(tree[u].size < x) return 0;
    	while(1)
    	{
    		int y = tree[u].son[0];
    		if(x <= tree[y].size) u = y;
    		else if(x <= tree[y].size + tree[u].tot) return tree[u].edge;
    		else x -= (tree[y].size + tree[u].tot), u = tree[u].son[1];
    	}
    }
    
  • 相关阅读:
    弹出框 popover.js
    模态框 modal.js
    关于css阴影和浮动
    css文件分类
    按钮效果
    二级导航栏
    在CSS3中,可以利用transform功能来实现文字或图像的旋转、缩放、倾斜、移动这四种类型的变形处理
    CSS3背景定位 background-origin
    什么是优雅降级和渐进增强
    居中
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/12275990.html
Copyright © 2011-2022 走看看