浅谈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)\)的,这时树就变成一条链了
这是就可以用平衡树解决.....以后或许会更一篇平衡树的博客(前提是我要学会)
参考《算法竞赛进阶指南》,代码未经测试不保证正确性,如有错误还望指正(狗头保命)
平衡树
浅谈BST(二叉查找树)的更多相关文章
- 浅谈算法和数据结构: 七 二叉查找树 八 平衡查找树之2-3树 九 平衡查找树之红黑树 十 平衡查找树之B树
http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html 前文介绍了符号表的两种实现,无序链表和有序数组,无序链表在插入的 ...
- HTTP协议漫谈 C#实现图(Graph) C#实现二叉查找树 浅谈进程同步和互斥的概念 C#实现平衡多路查找树(B树)
HTTP协议漫谈 简介 园子里已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲 ...
- 浅谈splay(点的操作)
浅谈splay(点的操作) 一.基本概念 splay本质:二叉查找树 特点:结点x的左子树权值都小于x的权值,右子树权值都大于x的权值 维护信息: 整棵树:root 当前根节点 sz书上所有结点编号 ...
- 浅谈 Java集合
Java 集合 集合是对象的容器,定义了多个对象进行操作的常用方法,可实现数组的功能. Java集合类库所处位置:java.util.*. 与现代的数据结构类库的常见做法一样,Java集合类库也将接口 ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- 浅谈Java的throw与throws
转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...
- 浅谈SQL注入风险 - 一个Login拿下Server
前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...
- 浅谈WebService的版本兼容性设计
在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...
随机推荐
- AbstractExecutorService源码
public class RunnableFutureTask { static FinalizableDelegatedExecutorService executorService = (Fina ...
- 基于hash的AB测试
AB测试 为同一个目标,设计两种方案,将两种方案随机投放市场中,让组成成分相同(相似)用户去随机体验两种方案之一,根据观测结果,判断哪个方案效果更好. 基于hash的AB测试 具体的实现方法为: 在A ...
- windows远程桌面无法拷贝文件的问题与解决方法
在开发完往windows服务器上部署系统或者给系统打补丁的时候,都会需要远程桌面的双向拷贝文件功能. 但是有些时候却会发现没有办法拷贝文件,原因主要有两个. 01 远程桌面的剪贴板设置 一个是在远程桌 ...
- 解决mac/win双系统,mac原生读写NTFS分区重启后失效的问题
安装mac/win双系统,然后在mac下启用原生的NTFS分区读写功能,并将分区创建桌面快捷方式后,会发现有时候进入win后再进mac,原来创建的分区桌面快捷方式是白色的图标,并且分区也无法打开,这个 ...
- 【BZOJ4833】最小公倍佩尔数(min-max容斥)
[BZOJ4833]最小公倍佩尔数(min-max容斥) 题面 BZOJ 题解 首先考虑怎么求\(f(n)\),考虑递推这个东西 \((1+\sqrt 2)(e(n-1)+f(n-1)\sqrt 2) ...
- React setState没有及时更新
1.封装 setFieldsValue 方法 /** * 更新state中的值 * data:{stateFieldName1:stateFieldvalue1...} */ setStateFiel ...
- 新一代ActiveMQ —— Apache ActiveMQ Artemis
资料: .net demo : https://github.com/apache/activemq-artemis/tree/master/examples/protocols/amqp/dotne ...
- WEB API 有效的Action定义
不能有特殊名称(例如属性访问器和运算符的重载方法) 的某些编译器以特殊方式处理的成员.可使用MethodInfo.IsSpecialName判断. 不能标记为[NonAction] 所在的类必须是Ap ...
- Python - 记录我开始学习Python的时间节点
记录我开始学习Python的时间节点 2019-09-22 从明天开始我要开始学习Python了,坚持学习.坚持写博客,慢慢积累. 结合实例项目,最好能把目前在做的项目用Python实现. 加油!
- JS基石之-----防抖节流函数
防抖和节流函数 阅读目录 一 .防抖函数 二 .节流函数 三 .个人理解两者的区别 一.防抖函数 1.1 概念: 触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算 ...