zoukankan      html  css  js  c++  java
  • 浅谈BST(二叉查找树)

    BST的性质

    树上每个节点上有个值,这个值叫关键码

    每个节点的关键码大于其任意左侧子节点的关键码,小于其任意右节点的关键码。

    显然一个BST的中序遍历就是关键码单调递增的节点序列

    BST的建立

    为了避免越界其实好像没卵用,减少边界情况的判定,一般在BST中额外插入一个关键码为INF和-INF的节点

    const int N=1000000;
    struct BST
    {
    	int l,r;//l,r分别是左右孩子的编号
    	int val;//关键码
    }a[N];
    int tot,root,INF=1<<30;
    int New(int val)
    {
    	a[++tot].val=val;
    	return tot;
    }
    void build()
    {
    	New(-INF),New(INF);
    	root=1,a[1].r=2;
    }
    

    BST的检索

    在BST中检索是否存在关键码为val的节点

    设p为根节点

    1.若p的关键码等于(val),直接返回

    2.若p的关键码大于(val)

    ​ (1)若(p)的左子节点为空,则不存在

    ​ (2)若(p)的左子节点不为空,则在p的左子树中递归检索

    2.若p的关键码小于(val)

    ​ (1)若(p)的右子节点为空,则不存在

    ​ (2)若(p)的右子节点不为空,则在(p)的右子树中递归检索

    int get(int p,int val)
    {
    	if(p==0) return 0;
    	if(val==a[p].val) return p;
    	return val<a[p].val ? get(a[p].l,val) : get(a[p].r,val);
    }
    

    BST的插入

    在BST中插入关键码为val的节点

    与BST的检索的检索过程类似,这里就不在赘述

    void insert(int &p,int val)
    {
    	if(p==0)
    	{
    		p=New(val);
    		return ;
    	}
    	if(val==a[p].val) return;
    	val<a[p].val ? insert(a[p].l,val) : insert(a[p].r,val); 
    }
    

    细心的读者应该发现了p是引用的,why?

    因为这里父节点的l或r值会被更新

    BST求前驱/后继

    这里首先赘述一下什么是前驱和后继

    后继:BST中关键码大于val的最小的

    前驱:BST中关键码小于val的最大的

    这里以求后继为例

    初始化ans为正无穷关键码的那个节点的编号。然后在BST中检索val,检索过程中不断更新ans

    检索完成后有三种可能的结果

    1.没有找到(val),那么现在ans即为所求

    2.找到了(val),但是关键码为val的节点p没有右子树,那ans即为所求

    3.找到了(val),而且关键码为val的节点p有右子树,那么就要从p的右孩子一直向左找

    因为教育局把sm.ms图床给封了所以图片上传不了,抱歉

    int getnext(int val)
    {
    	int ans=2,p=root;
    	while(p)
    	{
    		if(val==a[p].val)
    		{
    			if(a[p].r>0)
    			{
    				p=a[p].r;
    				while(a[p].l>0) p=a[p].l;
    				ans=p;
    			}
    			break;
    		}
    		if(a[p].val>val&&a[p].val<a[ans].val) ans=p;
    		p=val<a[p].val ? a[p].l : a[p].r;
    	}
    	return ans;
    }
    

    上面的代码是用的非递归,以后有时间可能会补上递归的

    前驱同理,这里不再赘述。

    BST的节点删除

    从BST中删除关键码为val的节点

    首先检索出关键码为val的节点p来

    有以下几种情况

    1.如果p的子节点个数小于(2),则直接删除掉(p),并令(p)的子节点替代(p)的位置,与(p)的父节点相连

    2.(p)既有左子树又有右子树,那么就找出(val)的后继来,显然后继没有左子树,所以直接删除后继,然后让后继的右子树代替游记,然后让后继代替(p)

    没法传图,抱歉*2

    void remove(int &p,int val)
    {
    	if(p==0) return ;
    	if(val==a[p].val)
    	{
    		if(a[p].l==0) p=a[p].r;
    		else if(a[p].r==0) p=a[p].l;
    		else 
    		{
    			int next=a[p].r;
    			while(a[next].l>0) next=a[p].l;
    			remove(a[p].r,a[next].val);
    			a[next].l=a[p].l,a[next].r=a[p].r;
    			p=next;
    		}
    		return ;
    	}
    	if(val<a[p].val) remove(a[p].l,val);
    	else remove(a[p].r,val);
    }
    

    细心的读者可能发现了,上述代码只能处理没有重复的关键值的情况,其实处理重复的关键值也很简单,这需要记录一个(cnt)即可

    struct BST
    {
    	int l,r;//l,r分别是左右孩子的编号
    	int val;//关键码
        int cnt;//计数器
    }a[N];
    

    其他操作不再赘述

    复杂度

    显然BST一次操作复杂度为(O(log N))

    但是BST容易被有序数列卡成(O(N))的,这时树就变成一条链了

    这是就可以用平衡树解决.....以后或许会更一篇平衡树的博客(前提是我要学会

    参考《算法竞赛进阶指南》,代码未经测试不保证正确性,如有错误还望指正(狗头保命

    平衡树

    浅谈fhq treap

  • 相关阅读:
    asp生成随机密码
    ASP用FSO生成HTML简单实例+详解 asp生成html 空白
    jquery ajax
    js屏蔽F5 兼容ie和firefox
    VS2003+SQL Server2000环境下出现的“未将对象引用设置到对象的实例”错误
    Hibernate开发笔记
    用SETUP FACTORY制作安装程序
    SPRING jdbc THIN方式 访问ORACLE数据库慢的解决方案
    JAVA如何调用DOS命令(转载)
    在WEBLOGIC9.1上部署SPRING+WEBWORK 的WEB应用
  • 原文地址:https://www.cnblogs.com/pyyyyyy/p/12067890.html
Copyright © 2011-2022 走看看