深度优先遍历
- 导读
- [一、2331. 计算布尔二叉树的值](#一、2331. 计算布尔二叉树的值)
-
- [1.1 题目介绍](#1.1 题目介绍)
- [1.2 解题思路](#1.2 解题思路)
- [1.3 代码编写](#1.3 代码编写)
-
- [1.3.1 定义函数](#1.3.1 定义函数)
- [1.3.2 递归基](#1.3.2 递归基)
- [1.3.3 递进关系](#1.3.3 递进关系)
- [1.3.4 组合优化](#1.3.4 组合优化)
- [1.4 代码测试](#1.4 代码测试)
- [二、814. 二叉树剪枝](#二、814. 二叉树剪枝)
-
- [2.1 题目介绍](#2.1 题目介绍)
- [2.2 解题思路](#2.2 解题思路)
- [2.3 代码编写](#2.3 代码编写)
-
- [2.3.1 函数定义](#2.3.1 函数定义)
- [2.3.2 递归基](#2.3.2 递归基)
- [2.3.3 递进关系](#2.3.3 递进关系)
- [2.3.4 组合优化](#2.3.4 组合优化)
- [2.4 代码测试](#2.4 代码测试)
- 结语

导读
大家好,很高兴又和大家见面啦!!!
在上一篇中,我们探讨了如何利用深度优先搜索 (DFS ) 的中序遍历 特性,在二叉搜索树中高效地查找第K小的元素 。我们看到了 DFS 如何通过递归自然地深入树的分支,系统地访问每个节点。
DFS 的核心思想在于"一路到底,再逐步回溯"。这种策略在解决树形结构的问题时尤为强大。
今天,我们将继续深入这一主题,通过两道经典的二叉树问题,进一步巩固 DFS 的理解与应用 ,特别关注其 后序遍历形式 的强大威力。
现在,让我们进入正文,一起探索 DFS 的实践魅力。
一、2331. 计算布尔二叉树的值
1.1 题目介绍
题目标签 :树、深度优先搜索、二叉树、第82场双周赛
题目难度 :简单
题目描述 :
给你一棵 完整二叉树 的根,这棵树有以下特征:
- 叶子节点 要么值为 0 要么值为 1 ,其中 0 表示
False,1 表示True。 - 非叶子节点 要么值为 2 要么值为 3 ,其中 2 表示逻辑或
OR,3 表示逻辑与AND。
计算 一个节点的值方式如下:
- 如果节点是个叶子节点,那么节点的 值 为它本身,即
True或者False。 - 否则,计算 两个孩子的节点值,然后将该节点的运算符对两个孩子值进行 运算 。
返回根节点 root 的布尔运算值。
完整二叉树 是每个节点有 0 个或者 2 个孩子的二叉树。
叶子节点 是没有孩子的节点。
示例 1 :
输入:root = [2, 1, 3, null, null, 0, 1]
输出:true
2
or 1 3
and 0 1
解释:ret = 1 or (0 and 1) = true。
AND 与运算节点的值为 False AND True = False 。
OR 运算节点的值为 True OR False = True 。
根节点的值为 True ,所以我们返回 true 。
示例 2 :
输入:root = [0]
输出:false
0
解释:ret = 0
根节点是叶子节点,且值为 false,所以我们返回 false 。
提示 :
树中节点数目在 [1, 1000] 之间。
0 ≤ N o d e . v a l ≤ 3 0 \leq Node.val \leq 3 0≤Node.val≤3
每个节点的孩子数为 0 或 2 。
叶子节点的值为 0 或 1 。
非叶子节点的值为 2 或 3 。
1.2 解题思路
该题比较简单,其结果主要由三部分构成:
- 左子树:值为
1或者0 - 根节点:运算符为
or或者and - 右子树:值为
1或者0
因此整棵树的值,我们只需要依次计算出左子树和右子树后,在将二者通过根节点的运算符完成运算即可;
而这个二叉树的左右子树均可以通过将其分解为三部分,即,树中的每棵子树的值均是由:
- 左子树的值
- 根节点的运算符
- 右子树的值
这三部分构成。这种 分而治之 的分解思想正是 递归 的算法思想,而 递归 在 数据结构 中的应用正是 深度优先搜索 算法。
在前面我们介绍过,二叉树 的 中序遍历 正是 DFS 的表现形式,但是这并不意味着 中序遍历 就是二叉树中的 DFS 。
对于二叉树中的 深度优先搜索 ,我们应该理解为:
- 深度优先 策略在 二叉树 中的应用
而该应用在 二叉树 中有三种表现形式:
- 先序
- 中序
- 后序
因此不管是哪种表现形式,我们均可以将其称为 二叉树 中的 深度优先 策略。当该策略 + 搜索目的后,就得到了 二叉树 中的 深度优先搜索;
在本题中,我们要想获得一棵树的布尔值,我们就需要先获取左右子树的值,在通过根节点将二者进行运算,因此该二叉树的遍历顺序为:
- 左子树 → 右子树 → 根结点 左子树 \rightarrow 右子树 \rightarrow 根结点 左子树→右子树→根结点
该遍历顺序对应的正是 二叉树 中的 后序遍历,也就是说我们要解决本题的具体方式为:
- 使用 深度优先策略 以 后序 的形式对该 二叉树 进行 遍历 获取 二叉树 的布尔值
1.3 代码编写
明确了具体的解题思路后,接下来我们就需要开始编写相应的代码了。
1.3.1 定义函数
该函数的目的是:
- 通过 深度优先 策略对该 二叉树 以 后序 的形式完成 遍历 获取树的布尔值;
因此我们可以采用多种命名方式:
DFT------ 深度优先搜索DFS------ 深度优先遍历Post_Order------ 后序遍历
这里我们还是使用 DFS 作为函数名,函数的返回类型为 bool ,函数的参数为树的根节点 root:
c
bool dfs(struct TreeNode* root) {
}
1.3.2 递归基
在 树 中,都是以判断树是否为空作为递归基,在这题中,我们则可以通过各结点的值作为函数的递归基:
c
if (root->val == 1) {
return true;
}
if (root->val == 0) {
return false;
}
if (root->val == 2) {
return left || right;
}
return left && right;
当结点的值为 0 表示该结点为叶子结点,且对应的值为 false;
当结点的值为 1 表示该结点为叶子结点,且对应的值为 true;
当结点的值为 2 表示该节点为非叶子结点,且对应的值为 left || right;
当结点的值为 3 表示该节点为非叶子结点,且对应的值为 left && right;
1.3.3 递进关系
在二叉树中,其递进关系就是该棵树的左右子树:
c
bool left = dfs(root->left);
bool right = dfs(root->right);
1.3.4 组合优化
当我们将上述内容进行整合,就得到了最终的代码:
c
bool dfs(struct TreeNode* root) {
if (root->val == 1) {
return true;
}
if (root->val == 0) {
return false;
}
bool left = dfs(root->left);
bool right = dfs(root->right);
if (root->val == 2) {
return left || right;
}
return left && right;
}
bool evaluateTree(struct TreeNode* root) {
return dfs(root);
}
1.4 代码测试
下面我们就在 leetcode 中对该代码进行测试:

二、814. 二叉树剪枝
2.1 题目介绍
题目标签 :树、深度优先搜索、二叉树
题目难度 :中等
题目描述 :
给你二叉树的根结点 root ,此外树的每个结点的值要么是 0 ,要么是 1。
返回移除了所有不包含 1 的子树的原二叉树。
节点 node 的子树为 node 本身加上所有 node 的后代。
示例 1 :
输入:root = [1, null, 0, 0, 1]
输出:[1, null, 0, null, 1]
剪枝后 1 NULL 0 NULL 1 原树 1 NULL 0 0 1
解释:
只有红色节点满足条件"所有不包含 1 的子树"。 右图为返回的答案。
示例 2 :
输入:root = [1, 0, 1, 0, 0, 0, 1]
输出:[1, null, 1, null, 1]
剪枝后 1 NULL 1 NULL 1 原树 1 0 1 0 0 0 1
示例 3 :
输入:root = [1, 1, 0, 1, 1, 0, 1, 0]
输出:[1, 1, 0, 1, 1, null, 1]
剪枝后 1 1 0 1 1 NULL 1 原树 1 1 0 1 1 0 1 0
提示 :
树中节点的数目在范围 [1, 200] 内
Node.val 为 0 或 1
2.2 解题思路
该题的解题思路很简单,我们判断一棵子树是否需要被剪枝,我们只需要判断其左右子树以及根结点中是否存在 1 :
- 存在,则不进行剪枝
- 不存在,则进行剪枝
因此,我们在决定是否要进行剪枝操作前,我们需要先检查该子树的左右子树,具体的操作算法,我们可以通过 深度优先 策略,并以 后序 的形式对该棵树进行 遍历;
2.3 代码编写
2.3.1 函数定义
该函数的目的为:
- 通过 深度优先 策略,并以 后序 的形式对该棵树进行 遍历
而遍历的目的是为了判断是否执行剪枝操作:
- 树中存在
1则不执行任何操作 - 树中不存在
1则执行剪枝操作
因此函数的返回类型为 bool:
c
bool DFS(struct TreeNode* root) {
}
2.3.2 递归基
在该函数中,我们需要根据左右子树以及根节点来判断是否需要进行剪枝:
c
if (root == NULL) {
return true;
}
if (left && right && root->val == 0) {
return true;
}
return false;
当该树为空树时,则表示该结点需要进行剪枝,即返回 true;
当树为非空树,且其左右子树均为需要剪枝,且该结点的值为 0 ,则表示该结点需要进行剪枝,即返回 true;
当树为非空树,且该结点的值为 1 ,则表示该结点不需要进行剪枝,即返回 false;
2.3.3 递进关系
在二叉树中,其递进关系为其左右子树:
c
bool left = DFS(root->left);
bool right = DFS(root->right);
2.3.4 组合优化
当我们将上面的内容进行整合,并加入剪枝操作后,即可得到完整的代码:
c
bool DFS(struct TreeNode** root) {
if (*root == NULL) {
return true;
}
bool left = DFS(&((*root)->left));
bool right = DFS(&((*root)->right));
if (left && right && (*root)->val == 0) {
free(*root);
*root = NULL;
return true;
}
return false;
}
struct TreeNode* pruneTree(struct TreeNode* root) {
bool ret = DFS(&root);
return root;
}
需要注意的是,由于剪枝操作是直接更改相应的二叉树,因此我们需要以指针的形式进行传参;
2.4 代码测试
接下来我们就来测试一下该代码:

结语
通过今天对两道 LeetCode 真题的深入剖析,我们进一步巩固了深度优先遍历 (DFS ) 在二叉树问题中的应用。从 2331.计算布尔二叉树的值 到 814.二叉树剪枝 ,我们看到了 DFS后序遍历 模式的强大威力。
关键收获总结:
-
问题分解思维:无论是布尔运算还是剪枝判断,都能通过递归自然分解为子问题
-
后序遍历的精髓:先处理子树,再根据子树结果决定当前节点操作------这正是"自底向上"的解决思路
-
实践出真知:通过具体编码,我们加深了对递归基、递推关系的理解
DFS的学习路径建议:
-
掌握三种遍历方式(前序、中序、后序)的适用场景
-
理解递归在树结构中的自然应用
-
通过不同难度题目逐步提升应用能力
深度优先遍历作为基础算法思想,其应用远不止于二叉树。掌握好这一利器,将为后续学习图论等更复杂的数据结构打下坚实基础。
互动与分享
-
点赞👍 - 您的认可是我持续创作的最大动力
-
收藏⭐ - 方便随时回顾这些重要的基础概念
-
转发↗️ - 分享给更多可能需要的朋友
-
评论💬 - 欢迎留下您的宝贵意见或想讨论的话题
感谢您的耐心阅读! 关注博主,不错过更多技术干货。我们下一篇再见!