文章目录
- 1. 前言
- 2. 算法题
- 2331.计算布尔二叉树的值
- 129.求根节点到叶节点数字之和
- LCR047.二叉树剪枝
- 98.验证二叉搜索树
- 230.二叉搜索树中第K小的元素
- 257.二叉树的所有路径
1. 前言
有关 递归 的相关解释与解题 请看下文:
以汉诺塔理解递归、并用递归解决算法题
-
对于二叉树,我们曾学过前序遍历,其就是深度优先搜索的一种应用。
- 在二叉树的前序遍历中,我们首先访问根节点,然后递归对左子树进行前序遍历,最后递归地对右子树进行前序遍历。
-
在深度优先搜索算法中,我们从起始节点开始,递归地探索每个可达节点,直到没有未访问的相邻节点为止。因此,前序遍历也可以看作是对图或树进行深度优先搜索的一种方式。它遵循先访问根节点,然后递归地访问左子节点和右子节点的顺序。
2. 算法题
2331.计算布尔二叉树的值
思路
- 题意分析:对于叶子节点有fals,true;对于非叶子节点有|、&;要求算出最终结果,我们只需要进行深搜,遍历一遍二叉树即可。
- 解法:递归dfs + 前序遍历
- 函数体:即前序遍历,分别用bool类型记录左右子树值
- 返回值:当前节点非叶子节点,根据其值判断将左右子树 或还是与
- 函数出口:即递归出口,当遍历到叶子节点后,通过其值向上返回bool类型。
代码
bool evaluateTree(TreeNode* root) {
// 递归
// 叶子节点为终止条件
if(root->left == nullptr && root->right == nullptr)
return root->val == 1 ? true : false;
bool left = evaluateTree(root->left);
bool right = evaluateTree(root->right);
return root->val == 2 ? left | right : left & right;
}
129.求根节点到叶节点数字之和
思路
- 题目要求计算二叉树中所有根节点到叶子节点的路径和,如示例所示,对于[1, 2, 3]的最终结果就是两条路径 12 + 13 = 25
- 解法:递归dfs
- 思路如上图所示,以此我们可以完成dfs函数:
- 函数头:需要接收一个节点以及到该节点时的路径值,且最终返回的也是总的路径值,即int
dfs(Node* root, int preSum)
- 函数体:先统计到当前节点的路径值,再进行左右子树的遍历
- 终止条件:当遍历到叶子节点,向上返回值
- 函数头:需要接收一个节点以及到该节点时的路径值,且最终返回的也是总的路径值,即int
代码
class Solution {
public:
// 返回到当前节点计算的所有值
int dfs(TreeNode* root, int prevSum) {
// 1. 计算prevSum和该节点的数字和
int sum = prevSum*10 + root->val;
// 2. 终止条件(叶子节点)
if(!root->left && !root->right) return sum;
// 3.递归左右子树
int left = root->left ? dfs(root->left, sum) : 0;
int right = root->right ? dfs(root->right, sum) : 0;
// 4. 计算出左右子树和并向上返回
return left + right;
}
int sumNumbers(TreeNode* root) {
if(!root) return 0;
// 递归
return dfs(root, 0);
}
};
LCR047.二叉树剪枝
思路
- 题意分析:题目要求删除二叉树中所有不包含1的子树,则可以利用递归从后向前进行删除。(即通过后序遍历 删除值为0的叶子节点)
- 解法:递归dfs + 后序遍历
- 函数体:后续遍历,当遇到值为0的叶子节点,将该节点值设为空
- 函数出口:当遍历到空指针时,向上返回。
代码
class Solution {
public:
TreeNode* pruneTree(TreeNode* root) {
// 后序遍历
if(root == nullptr) return nullptr; // 向上返回
root->left = pruneTree(root->left);
root->right = pruneTree(root->right);
if(!root->left && !root->right && root->val == 0) {
// delete root; // 释放内存:防止内存泄漏
root = nullptr; // 置空
}
return root;
}
};
98.验证二叉搜索树
思路
- 题意分析:题目要求验证一棵树是否是二叉搜索树。
- 解法:递归dfs + 中序遍历 + 利用二叉搜索树性质
- 而我们知道,根据二叉搜索树的定义,其中序遍历是有序的,对于BSTree的任意一个节点,其前驱节点一定小于自身
- 据此,我们可以利用中序遍历以及该性质解题:
- 定义 前驱节点以及一个标记符flag用于标记当前节点是否满足条件。
- 函数体:中序遍历,对于当前节点判断:如果其前驱节点存在且大于自身,则不是BSTree,将标记符设为false,递归结束。
- 结束条件:当遍历到空节点或标记符为false时,向上返回
- 而我们知道,根据二叉搜索树的定义,其中序遍历是有序的,对于BSTree的任意一个节点,其前驱节点一定小于自身
代码
class Solution {
public:
TreeNode* prev = nullptr; // 定义全局变量,用于找前驱节点
bool flag = true;; // 标记树是否是bstree
bool isValidBST(TreeNode* root) {
// 递归
if(root != nullptr && flag)
{
// 中序遍历
isValidBST(root->left);
// 如果前一个节点的值大于当前节点的值,则不满足二叉排序树的条件
if(prev != nullptr && prev->val >= root->val)
flag = false;
prev = root; // 更新前驱节点
isValidBST(root->right);
}
return flag;
}
};
230.二叉搜索树中第K小的元素
思路
- 题意分析:题目要求找到二叉搜索树的倒数第k个最小元素,依照上题的思路,进行中序遍历即可。
- 解法:递归dfs + 中序遍历 + 利用二叉搜索树性质
- 定义全局变量,省去多次递归创建变量的开销:定义count和ret分别记录k值和结果值
- dfs函数中进行中序遍历,每次递归–count,直到count==0,此时节点的值就是ret
代码
class Solution {
public:
// 全局变量解决递归问题
int count, ret;
void dfs(TreeNode* root) {
// 中序遍历
if(!root || count == 0) return;
dfs(root->left);
--count;
if(count == 0)
ret = root->val;
dfs(root->right);
}
int kthSmallest(TreeNode* root, int k) {
count = k;
dfs(root);
return ret;
}
};
257.二叉树的所有路径
思路
- 题意分析:输出所有从根节点到叶子节点的路径。
- 思路:思路很简单,就是直接使用前序遍历即可,对每个节点都加入到字符串中并对字符串后加箭头。
- 解法:递归dfs + 前序遍历
- 细节问题:
- 叶子节点不需要加箭头,写代码时另外列出
- 其余则是对vector和string的使用
- 细节问题:
代码
class Solution {
public:
vector<string> ret; // 最终结果
// 如果将path定义为全局变量,则需要手动"恢复现场"
// 而作为函数参数,则由函数的性质自动完成了此过程
void dfs(TreeNode* root, string path) {
// 前序遍历
if(root == nullptr) return;
// 叶子结点不需要箭头
// 将其加入到ret中,并返回
if(!root->left && !root->right)
{
path += to_string(root->val);
ret.push_back(path);
return;
}
path += to_string(root->val) + "->";
dfs(root->left, path);
dfs(root->right, path);
}
vector<string> binaryTreePaths(TreeNode* root) {
string path = ""; // 用于记录当前路径
dfs(root, path);
return ret;
}
};