二叉搜索树
二叉搜索树学习笔记
这篇文章是学习平衡树的铺垫
什么是二叉搜索树
二叉搜索树,又名二叉查找树、二叉排序树,是满足以下性质的一种树形结构
- 每一个节点的左子树里的所有节点上的权值,都比这个节点的权值小
- 每一个节点的右子树里的所有节点上的权值,都比这个节点的权值大
举个例子,就长这样:
其中每个节点都有编号和权值(还有一些其它玩意儿),所以定义结构体存节点
struct Node{
int l,r;//左右儿子的编号
int val;//这个节点的权值
int cnt;//计数器,有用的
}tr[N];
一个简单的结论
有了刚才那样特殊的性质,我们只要稍微思考一下就能得出以下结论:
- 一颗二叉排序树的中序遍历的顺序是从小到大排好序的
为什么哩?
所谓中序遍历,就是说按照左根右的顺序遍历一棵树
先遍历左子树,再遍历根,再遍历右子树
加上刚才的二叉搜索树的定义,左子树都小,右子树都大,很容易得出结论
例如:刚才上面那棵树的中序遍历就是
简易代码
void Medium_order(int u)
{
if(tr[u].l)
Medium_order(tr[u].l);
cout<<tr[u].val<<' ';
if(tr[u].r)
Medium_order(tr[u].r);
}
And...then?
所以,这个结论到底有用嘛……
当然有用!有了杰个结论,二叉搜索树就能动态维护一个有序的数列,并支持以下操作
支持的操作
插入一个节点 删除一个节点
以上是没有技术含量的基本操作
插入 insert
插入的基本思路
就是从根节点开始看
如果要插入的结点的值比当前考虑的节点的值小,递归到左子树插入
否则如果比当前的值大,递归到右子树插入
到最后如果找到了一个和待插入的节点的值相同的节点,把当前节点的计数器
如果到最后也没有找到相同的节点,说明这样的结点之前并不存在,新开一个节点即可
图示
# 1 插入一个节点,值为
# 2 插入一个节点,值为
代码
先写一个开新节点的函数
int newnode(int x)
{
idx++;
tr[idx].val=x;
return idx;
}
void insert(int p,int x)
{
if(x<tr[p].val)
{
if(tr[p].l)
insert(tr[p].l,x);
else
tr[p].l=newnode(x);
}else if(x>tr[p].val){
if(tr[p].r)
insert(tr[p].r,x);
else
tr[p].r=newnode(x);
}else tr[p].cnt++;
}
删除 remove
删除叶节点非常好办,直接找到之后
但是不是叶子的话就麻烦了,对于我们学习平衡树也没有太大作用
所以这一部分就咕咕掉了(平衡树删除跟它一点关系都没,也挺好办的)
基本操作到此结束,下面来一些高级点的
查询最大值 查询最小值
最大值 & 最小值
思路非常简单
每一个点的右子树里所有点权值都比这个点大
所以从根节点开始找,一直往右走,不撞南墙不回头
走到的最后一个点就是最大值
最小值同理,一直向左走,走到不能走为止
图示
代码
代码咕咕咕喽 ψ(._. )>
嘿嘿(毕竟是平衡树为平衡树的铺垫,扯得太多了)
二阶操作结速,看看最高级操作
-
求一个节点的前驱 -
求一个节点的后继
某个节点的前驱和后继就是中序遍历中这个节点前面和后面的节点
换句话说就是
权值小于该节点的点中权值最大的
and
权值大于该节点的点中权值最小的
怎么求呢?
求前驱 get_prev
分类讨论
如果一个点,他有左子树,那么显然他的前驱就在左子树里,而且还是左子树里的最大的那个值
那么我们只要先向左走一步,走到到左儿子
然后按照刚才说的找最大值的思路,在左子树中一直向右走,走到不能走为止就行辣;
那……如果这个点的左子树为空呢?
那就又要分类讨论了
第一种情况:
如果该点是其父节点的右儿子,那么父节点就是它的前驱
证明:
没有左子树又是父亲的右儿子,就说明,除了 父亲节点 和 父亲的左子树里的所有点 之外,没有其他点的权值比他小了
而 父节点 的权值又比 父节点的左子树里的所有点的权值们 都要大
所以它的父节点就是他的前驱
第二种情况
如果是父节点的左儿子,就一路直着往上跳,跳到第一次拐弯就停
拐弯的意思就是原来都是从左儿子往上跳,直到有一次,是从右儿子向上跳了
也就是这样
图示
刚才的图太简陋了,包含的情况不全,再重新画一颗复杂一点的树
为了方便检验答案的正确性,我们直接把权值置为连续的自然数
# 1 首先找
# 2 再找
# 3 最后找
求后继 get_next
思路
刚好和求前驱反过来
有右儿子的话,就是右子树里的最小值
没有的话,如果是父亲的左儿子,那后继就是父亲,父亲就是后继
如果是右儿子,就一直往上跳,直到拐弯为止
应该很好理解吧……
图示?
和上面求前驱的图一样,就是把箭头反过来……
因为一个点一定是 这个点的前驱 的后继(废话文学)
尾声
二叉搜索树
到这里就讲完了
其实还没有结束
因为二叉搜索树有一个致命的缺点!
他需要改进
这里我卖个关子,等到下一篇平衡树里再说
(白白bye/)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」