实验课2 二叉树及其遍历 R代码整理¶
对应材料:
实验/实验课件/实验课2-二叉树及其遍历.pptx回放/生物信息学(协和班)第5周星期4第8,9节_笔记.txt- 老师板书整理
本节考试要求¶
1. 本节要求¶
- 这节实验课属于
R语言高级编程。 - 内容主线是:
- 理解二叉树结构
- 理解先序 / 中序 / 后序遍历
- 会用
list表示节点 - 会写递归函数完成:
- 创建节点
- 插入节点
- 搜索节点
- 找最小节点
- 删除节点
- 遍历输出
2. 考试掌握程度¶
- 老师明确说:这节课比实验课 1 难,属于“中等偏下到中等”的算法题,不是最基础的编程。
- 但老师也明确说:期末不会考得这么完整、这么难。
- 真正要求是:
- 要理解二叉树的逻辑
- 要理解递归
- 要能看懂伪代码并能写出核心函数思路
3. 老师重点强调¶
- 这节课最核心的关键词就是:
递归。 - 板书里老师重点写了两个函数:
insertNode()searchNode()- 老师反复强调:
- 前面几个函数如果理解递归,其实代码并不多
- 最难的是
deleteNode()里“用右子树最小值替换当前节点,再删掉那个最小值”这两步逻辑 - 遍历部分里,老师说:
- 先序 / 中序 / 后序都要理解
- 但这节实验课实际要求输出的是中序遍历结果
4. 与期末重点重合¶
- 高度重合,优先复习:这节课属于老师最后两节课里明确点名的“稍复杂一点的编程题 / 高级编程思想”。
- 与期末重点中的“基础编程 + 稍复杂编程 + 实验课代码思路”直接重合。
- 虽然老师说不一定完整考一棵树的所有函数,但:
递归查找插入中序遍历这些都很值得重点复习。
一、结合板书、课件和回放整理后的主干 R 代码¶
# 1. 创建节点
createNode <- function(value) {
list(
value = value,
left = NULL,
right = NULL
)
}
# 2. 插入节点
insertNode <- function(node, value) {
if (is.null(node)) {
return(createNode(value))
}
if (value <= node$value) {
node$left <- insertNode(node$left, value)
} else {
node$right <- insertNode(node$right, value)
}
return(node)
}
# 3. 搜索节点
searchNode <- function(node, value) {
if (is.null(node) || node$value == value) {
return(node)
}
if (value < node$value) {
return(searchNode(node$left, value))
} else {
return(searchNode(node$right, value))
}
}
# 4. 找最小值节点
findMinNode <- function(node) {
current <- node
while (!is.null(current$left)) {
current <- current$left
}
return(current)
}
# 5. 删除节点
deleteNode <- function(node, value) {
if (is.null(node)) {
return(NULL)
}
if (value < node$value) {
node$left <- deleteNode(node$left, value)
} else if (value > node$value) {
node$right <- deleteNode(node$right, value)
} else {
if (is.null(node$left)) {
return(node$right)
} else if (is.null(node$right)) {
return(node$left)
} else {
minNode <- findMinNode(node$right)
node$value <- minNode$value
node$right <- deleteNode(node$right, minNode$value)
}
}
return(node)
}
# 6. 中序遍历:左 -> 根 -> 右
inorderTraversal <- function(node) {
if (is.null(node)) {
return(NULL)
}
c(
inorderTraversal(node$left),
node$value,
inorderTraversal(node$right)
)
}
# 7. 先序遍历:根 -> 左 -> 右
preorderTraversal <- function(node) {
if (is.null(node)) {
return(NULL)
}
c(
node$value,
preorderTraversal(node$left),
preorderTraversal(node$right)
)
}
# 8. 后序遍历:左 -> 右 -> 根
postorderTraversal <- function(node) {
if (is.null(node)) {
return(NULL)
}
c(
postorderTraversal(node$left),
postorderTraversal(node$right),
node$value
)
}
# 9. 主程序:创建一棵树
root <- createNode(50)
for (v in c(30, 20, 40, 70, 60, 80)) {
root <- insertNode(root, v)
}
# 原始中序遍历
inorderTraversal(root)
# 删除 20 后的中序遍历
root <- deleteNode(root, 20)
inorderTraversal(root)
# 删除 30 后的中序遍历
root <- deleteNode(root, 30)
inorderTraversal(root)
# 删除 50 后的中序遍历
root <- deleteNode(root, 50)
inorderTraversal(root)
# 搜索示例
searchNode(root, 60)
二、板书里最核心的两个函数¶
老师板书最重点写的是这两个:
1. insertNode()¶
板书逻辑就是:
insertNode <- function(node, value) {
if (is.null(node)) {
return(createNode(value))
}
if (value <= node$value) {
node$left <- insertNode(node$left, value)
} else {
node$right <- insertNode(node$right, value)
}
return(node)
}
这段代码最重要的点有两个:
- 如果当前节点为空,就在这里新建节点
- 如果不为空,就根据大小决定往左子树还是右子树继续插
这就是典型递归:
- 你只写了一次
insertNode - 但它会不断调用自己,直到找到空位置
2. searchNode()¶
板书逻辑就是:
searchNode <- function(node, value) {
if (is.null(node) || node$value == value) {
return(node)
}
if (value < node$value) {
return(searchNode(node$left, value))
} else {
return(searchNode(node$right, value))
}
}
这段代码的核心是:
- 空节点:返回空
- 找到了:直接返回当前节点
- 目标值更小:去左边找
- 目标值更大:去右边找
三、这节课每一部分在考什么¶
1. 二叉树结构¶
- 每个节点最多两个子树:左子树、右子树
- 课件和回放里都在强调:
- 二叉树可以是空的
- 也可以只有根节点
- 也可以只有左子树或右子树
2. 遍历的三个核心顺序¶
课件写的是:
DLR:先序遍历LDR:中序遍历LRD:后序遍历
老师回放里反复强调:
- 它们的区别,关键看“根节点在什么时候被访问”
- 根节点第一次访问:先序
- 根节点中间访问:中序
- 根节点最后访问:后序
3. 二叉搜索树的大小关系¶
这节课默认是在讲二叉搜索树,核心规律是:
老师在回放里几乎一直围绕这个规律在讲:
- 插入为什么走左/右
- 查找为什么走左/右
- 删除为什么要找右子树最小值
4. 递归¶
这是本节最核心的概念。
老师反复强调:
- 不理解递归,这些函数会觉得很复杂
- 理解递归之后,代码其实很短
你可以把递归理解成:
- 当前问题如果解决不了
- 就把同样的问题交给“更小的一棵子树”
- 直到碰到边界条件(比如
NULL)为止
四、删除节点为什么最难¶
老师明确说,难点主要就在 deleteNode() 最后那两步。
删除节点分三种情况:
1. 这个节点没有左子树¶
- 直接返回右子树
2. 这个节点没有右子树¶
- 直接返回左子树
3. 这个节点左右子树都有¶
这是最难的情况:
- 去右子树里找最小值节点
- 用这个最小值替换当前节点值
- 再到右子树中把这个最小值原来那个节点删掉
比如课件主程序里删 50:
- 找到
50 - 它左右子树都存在
- 去右子树找最小值,得到
60 - 用
60替换50 - 再把原来的
60节点删掉
这就是老师回放里说的“最难的是后面那两行逻辑”。
五、老师实际要求你做到什么程度¶
1. 不要求你把整套代码都默写得非常长¶
- 老师明确说:期末不会考这么完整、这么难。
- 但“不会完整照抄考”不等于“可以不看”。
2. 要理解伪代码¶
老师在回放里说得很明确:
- 要学会看课件上的“伪代码”
- 因为伪代码基本上就是代码思路本身
也就是说,你至少要做到:
- 看懂课件上的
IF / ELSE / RETURN - 能翻成
R代码
3. 真正最该掌握的是这些点¶
- 节点怎么表示
- 为什么节点用
list - 递归的边界条件是什么
- 插入为什么左小右大
- 搜索为什么可以只走一条路径
- 中序遍历为什么能输出有序结果
- 删除双子节点时为什么找“右子树最小值”
4. 这节课更像“大编程思路训练”¶
老师把它定义成:
- 不是最基础的编程
- 也不是特别高难度
- 是一种典型的中等算法题
而且老师还举了应用化变形的例子:
- 不一定考“数值”
- 也可能换成“学生成绩”“某个值的搜索”
- 本质还是同一个二叉搜索树逻辑
六、最适合考试背的最短主线¶
七、这节课最容易被问的点¶
- 什么是二叉树
- 什么是二叉搜索树
- 先序 / 中序 / 后序分别是什么
- 为什么中序遍历能得到从小到大的结果
- 递归的结束条件是什么
insertNode()为什么最后要return(node)searchNode()为什么只走左边或右边,不需要两边都找- 删除有两个子节点的节点时,为什么要找右子树最小值
八、如果考试让你用文字描述,可以这样写¶
先定义节点结构,每个节点包含
value、left、right三部分。然后利用插入函数递归构建二叉搜索树,满足左子树值小于根节点值、右子树值大于根节点值。查找节点时,根据目标值与当前节点值的大小关系递归进入左子树或右子树。删除节点时,若目标节点只有一个子树,则直接返回该子树;若左右子树都存在,则用右子树中的最小值节点替换当前节点,再递归删除右子树中的该最小值节点。遍历时可用递归实现先序、中序和后序,其中中序遍历顺序为左子树、根节点、右子树。
九、一句总结¶
这节实验课表面上是在写二叉树,实际上老师最想让你学会的是:如何把一个稍复杂的算法结构拆成多个递归函数来写。真正的核心不是背树,而是理解递归、理解左小右大、理解删除节点时的替换逻辑。