(d)二叉树实现

官官方信息技术老师·14 页·深入(追求细节与边界)·0 次浏览·38 天前
数据结构C++实现二叉树

二叉树的代码实现

看完你能亲手写出 BinNode 与 BinTree 的完整结构并说清每个设计取舍

按 空格/→ 演示下一步

问一句
1 / 14 页

全部页面点击任意一页,跳回舞台从这页播放

数据结构C++实现二叉树

二叉树的代码实现

看完你能亲手写出 BinNode 与 BinTree 的完整结构并说清每个设计取舍

1第 1 页 · 二叉树的代码实现

BinNode 是什么

上一页说了二叉树要在代码里落地,那每个节点长什么样?想想你的家族谱系:一个格子记录你自己的信息,旁边标着父亲是谁、上面是长子、下面是次子——BinNode 就是这样一个格子。

data
节点自身存放的数据,对应谱系里的人名
parent
指向父节点的指针,根节点此处为空
lc
左孩子指针,没有左孩子就置空
rc
右孩子指针,与 lc 互为兄弟分支
家族谱系格子对应 →BinNode 三个成员

格子里的姓名对应 data,父亲栏对应 parent,两栏子嗣对应 lc、rc

2第 2 页 · BinNode 是什么

BinNode 类定义

python

逐行看 BinNode 的五个成员变量,每个字段对应树结构的一部分。

代码高亮加载中…

五个字段各司其职:data 存内容,parent/lChild/rChild 三条引用搭出树形,height 为 AVL 等平衡操作预留。

3第 3 页 · BinNode 类定义

BinNode 接口

前两页我们写出了 BinNode 类的字段:数据、左右孩子、父亲。但用这个节点的人不该直接摸它的内部字段——就像遥控器只给你几个按键,电路细节都藏在里面。

状态判定
isLeaf 判断是否叶子、isLChild/isRChild 判断自己是左还是右孩子
关系访问
lChild()/rChild()/parent() 取出对应节点,没有则返回 null
数据访问
data() 读节点值,setData() 写入新值,读写分离
规模查询
size() 统计以自身为根的子树节点总数
遥控器按键对应 →BinNode 接口

按键对应接口方法,电视内部电路对应私有字段

4第 4 页 · BinNode 接口

接口的实现细节

cpp

宏封装胜过重复:一行宏生成接口,内联实现在头文件里完成,零调用开销。

代码高亮加载中…

宏让 BinNodePosi(T) 全程统一写法;类内定义即隐式 inline,size/insert/isLChild 这些高频小函数零调用开销,是接口实现的核心取舍。

5第 5 页 · 接口的实现细节

BinTree 类

前面我们把每个节点 BinNode 造好了:它能存数据、认得左右孩子和父亲。但节点只是散落的砖块,还缺一堵墙——谁来记住根在哪?树总共多高?这一页看 BinTree 如何搭起整体框架。

根指针 _root
树对象里只存一个入口,其余节点全靠它顺藤摸瓜
规模 _size
记录节点总数,免去每次遍历点数的开销
委托模式
插入、删除等具体操作转手交给相应节点去做
空树约定
构造时 _root 为空、_size 为零,是合法的树
公司前台对应 →BinTree 框架

前台只记公司大门(根)和人数(规模),具体事务转给各部门(节点)

6第 6 页 · BinTree 类

updateHeight 实现

java

看一个节点的高度如何计算:取左右子树高度的较大者,再加一。

代码高亮加载中…

stature 统一处理空子树返回 -1,这样叶子取 max 后加一正好得 0;高度变化才返回 true,供上层判断是否继续向上更新。

7第 7 页 · updateHeight 实现

updateHeightAbove 传播

局部调整后,高度变化要沿祖先链逐层上传,直到不再变化为止。

1
从parent出发
updateHeight后取x=parent(x),传播的起点是父亲节点
2
自查高度
对当前节点调updateHeight,看返回值是否变化
3
未变即终止
高度不变说明更高祖先不受影响,循环结束
4
变了继续上溯
高度变了则x=x.parent,继续检查上一层
5
至根为止
x为null时停止,全链高度已更新
8第 8 页 · updateHeightAbove 传播

insertAsRoot

insertAsRoot 是让空树「出生」的第一步:只允许在空树上调用。

1
先检查是否为空
确认 _root == nullptr,非空树调用无意义,需返回或断言
2
new 出新节点
用 new BinNode(e) 堆上分配,data 存 e,指针初始为空
3
设为根并计数
_root 指向新节点,_size 加一,这是唯一入口
4
更新高度
新叶节点高度为 0,调用 updateHeight 初始化
5
返回节点指针
返回 _root,调用方拿到句柄便于后续扩展
9第 9 页 · insertAsRoot

insertAsLC / insertAsRC

cpp

作为左/右孩子插入新节点:规模计数、节点接线、祖先高度传播,三处协作各干一件事。

代码高亮加载中…

树的职责(计数、更新高度)与节点的职责(接线)清晰分工:BinNode 一行完成孩子挂接,BinTree 负责全局信息的维护。

10第 10 页 · insertAsLC / insertAsRC

插入全流程图解

从调用插入到高度传回,箭头即执行顺序,编号标注四个阶段,自上而下走完整个链路。

图解渲染中…
c1:新节点parent指向父亲xc2:父亲x的lc指向新节点,双向接线缺一不可e1:从x出发沿parent逐层向上传播g1:插入接口统一返回新节点引用
11第 11 页 · 插入全流程图解

自测:插入后谁变高

点击作答

在叶子节点 x 的左孩子位置插入新节点后,哪些节点的高度会被 updateHeightAbove 更新?

12第 12 页 · 自测:插入后谁变高

要点收束

  • ✓节点存数据与指针,树只是持有根
  • ✓高度是懒更新:局部算,向上传播
  • ✓插入分三步:建节点、接指针、更新高度
  • ✓新节点祖先链都要 updateHeightAbove
  • ✓边界先想全:空树、已有孩子、高度持平
延伸主题:遍历:先序、中序、后序二叉搜索树的查找与插入失衡与 AVL 重平衡
13第 13 页 · 要点收束

课后思考

先合上讲义自己想,再对照参考答案,看看思路差在哪。

1updateHeightAbove 为什么从当前节点沿父链上行就停?什么条件下不会提前停?

参考答案提示:高度只在子树高度真正变化时才需继续传播。想想 insertAsLC 后新子树高度从 -1 变 0,父节点若因此长高才继续上行;一旦高度不变即可停,最好情况 O(1)。

2如果把 x 当作「空孩子」统一插入,insertAsLC/insertAsRC 还需要分开写吗?

参考答案可以让节点携带哨兵式空节点(类似 BST 的叶哨兵),统一一个插入接口、统一 updateHeight 触发点。代价是每个节点多存空孩子、遍历多判一次;想想哪种场景值得换。

3秩、父指针、高度够用吗?若要支持 O(1) 求子树规模,BinNode 该怎么改?

参考答案类比高度上行传播:增删时沿父链维护 size 也行,但删除无法沿链回退,得在插入时为每个祖先 +1 或在节点里存 size 修改入口。权衡:空间换时间,字段越多维护面越大。

14第 14 页 · 课后思考