灵神【基础算法精讲】视频的个人笔记。
问题
- 如何思考二叉树相关问题?
- 为什么需要使用递归?
- 为什么这样写就一定能算出正确答案?
- 计算机是怎么执行递归的?
- 另一种递归思路
求二叉树的最大深度
flowchart TB
3 --> 9 & 20
20 --> 15 & 7
不要一开始就陷入细节,而是思考子问题:整棵树与其左右子树的关系
flowchart TB
3 --> 左子树 & 右子树
整棵树的最大深度 = max(左子树的最大深度, 右子树的最大深度) + 1
此时将视角放到右子树上,同样可以用该公式来求出右子树的最大深度,计算的方式一样
flowchart TB
20 --> 15 & 7
- 原问题:计算整棵树的最大深度
- 子问题:计算左、右子树的最大深度 此时原问题拆解成了两个子问题,子问题还能继续拆解成两个问题, 一直到没有结点为止,这就是边界条件(base case)。
计算方式一样,那么代码肯定也一样,这种问题就适合用递归。
int maxDepth(TreeNode* root) {
if(root == nullptr) return 0;
return max(maxDepth(root -> left), maxDepth(root -> right)) + 1;
}
另一种递归思路
结果不用返回值,用全局变量
递归过程传参数当前层数,更新结果。
int res = 0;
void dfs(TreeNode* root, int cnt) {
if(root == nullptr) return;
cnt++;
res = max(res, cnt);
dfs(root -> left, cnt);
dfs(root -> right, cnt);
}
课后作业
111. 二叉树的最小深度 - 力扣(LeetCode)
class Solution {
public:
int DFS(TreeNode* root) {
if(root == nullptr) return 0;
//左右子树都为空
if(root->left == nullptr && root->right == nullptr)
return 1;
//左右子树其中一个为空
if(root->left == nullptr || root->right == nullptr)
return DFS(root->left) + DFS(root->right) + 1;
//左右子树都不为空
return min(DFS(root->left), DFS(root->right)) + 1;
}
int minDepth(TreeNode* root) {
return DFS(root);
}
};
112. 路径总和 - 力扣(LeetCode)
class Solution {
public:
bool dfs(TreeNode* root, int t) {
if(root == nullptr) return false;
if(root->left == nullptr && root->right == nullptr) //叶子节点
if(t == root->val)
return true;
//左右子树 其中一子树满足条件就行
return dfs(root->left, t - root->val) || dfs(root->right, t - root->val);
}
bool hasPathSum(TreeNode* root, int targetSum) {
return dfs(root, targetSum);
}
};
113. 路径总和 II - 力扣(LeetCode)
该题用了递归的第二种思路,结果和路径是类变量
class Solution {
public:
vector<int> path; //路径
vector<vector<int>> ans;
void dfs(TreeNode* root, int t) {
if(root == nullptr) return;
path.push_back(root->val); //保存路径
if(root->left == nullptr && root->right == nullptr) //叶子节点
if(root->val == t)
ans.push_back(path);
dfs(root->left, t - root->val);
dfs(root->right, t - root->val);
path.pop_back(); //退出路径
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
dfs(root, targetSum);
return ans;
}
};
129. 求根节点到叶节点数字之和 - 力扣(LeetCode)
这题跟上一题很像,可以继续用一样的思路。 LeetCode官方题解中,有不使用全局变量的解法。
class Solution {
public:
int path = 0; //路径
int ans = 0;
void dfs(TreeNode* root) {
if(root == nullptr) return;
path *= 10;
path += root->val; //保存路径
//叶子节点
if(root->left == nullptr && root->right == nullptr) {
ans += path;
}
dfs(root->left);
dfs(root->right);
path /= 10; //退出路径
}
int sumNumbers(TreeNode* root) {
dfs(root);
return ans;
}
};
257. 二叉树的所有路径 - 力扣(LeetCode)
官方题解中的深搜,先加root->val,不为叶子结点才加->。
我就把val和->一起加,会麻烦一点。
这题字符串回溯比较麻烦,就作为参数了。
class Solution {
public:
vector<string> ans;
void dfs(TreeNode* root, string path) {
if(root == nullptr) return;
if(root->left == nullptr && root->right == nullptr) { //叶子节点
ans.emplace_back(path + "->" + to_string(root->val));
}
string newPath = path == "" ?
to_string(root->val) :
path + "->"+ to_string(root->val);
dfs(root->left, newPath);
dfs(root->right, newPath);
}
vector<string> binaryTreePaths(TreeNode* root) {
if(root->left == nullptr && root->right == nullptr) {
return vector<string>{to_string(root->val)};
}
dfs(root, "");
return ans;
}
};