用对称性递归解决问题
问题分类
- 不需要构造辅助函数。这种问题分为两类,一类是单树问题,且不需要用到子树的某一部分,只要利用根节点左右子树的对称性即可进行递归。第二种是双树问题,即本身题目要求比较两棵树
- 需要构造辅助函数。这类题目通常只用根节点子树对称性无法完全解决问题,必须要用到子树的某一部分进行递归,即要调用辅助函数比较两个部分子树
不需要构造辅助函数
leetcode.100
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (!p && !q) return true;
return p && q && p->val == q->val && isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
leetcode.104
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
leetcode.965
class Solution {
public:
bool isUnivalTree(TreeNode* root) {
if (!root) return true;
if ((root->left && root->left->val != root->val) || (root->right && root->right->val != root->val)) return false;
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
};
leetcode.559
class Solution {
public:
int maxDepth(Node* root) {
if (!root) return 0
int depth = 0
for (auto c: root->children)
depth = max(depth, maxDepth(c))
return depth + 1
}
}
leetcode.111
- 链接leetcode.cn/problems/mi…
- 解题方法:
特判:根节点不存在直接返回0,如果不存在儿子节点返回1
逻辑:左儿子存在,每递归一次都更新答案,右儿子存在同样每递归一次更新答案
返回值:返回最大深度
- leetcode解题代码
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) return 0
int res = INT_MAX
if (!root->left && !root->right) res = 1
if (root->left) res = min(res, minDepth(root->left) + 1)
if (root->right) res = min(res, minDepth(root->right) + 1)
return res
}
}
leetcode.222
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
leetcode.404
- 链接leetcode.cn/problems/su…
- 解题方法:
特判:根节点不存在或只有根节点不存在叶子节点直接返回0
逻辑:根节点的左儿子如果为叶子节点记录根节点左儿子叶子节点的总和,同样记录根节点右儿子为叶子节点的总和,相加即为答案
返回值:返回答案
- leetcode解题代码
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return 0;
int sum_l = sumOfLeftLeaves(root->left);
if (root->left && !root->left->left && !root->left->right)
sum_l += root->left->val;
int sum_r = sumOfLeftLeaves(root->right);
return sum_l + sum_r;
}
};
需要构造辅助函数
leetcode.101
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return dfs(root->left, root->right);
}
bool dfs(TreeNode* p, TreeNode* q){
if (!p && !q) return true;
if (!p || !q) return false;
return p && q && p->val == q->val && dfs(p->left, q->right) && dfs(p->right, q->left);
}
};
leetcode.110
- 链接leetcode.cn/problems/ba…
- 解题方法:
辅助函数:返回以左右子树为根节点的子树深度的最大值
主函数特判:空树为平衡二叉树
返回值:平衡二叉树定义(左右子树高度差<=1 + 左子树为平衡二叉树+右子树为平衡二叉树)
- leetcode解题代码
class Solution {
public:
bool isBalanced(TreeNode* root) {
if (!root) return true;
return abs(depth(root->left) - depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
int depth(TreeNode* root){
if (!root) return 0;
return max(depth(root->left), depth(root->right)) + 1;
}
};
leetcode.572
- 链接leetcode.cn/problems/su…
- 解题方法:
辅助函数:判断root的左右子树为根节点和subroot是否相同
主函数特判:如果root和subroot都为空则返回false
如果满足辅助函数则返回true
返回值:subroot是否是root的子树(subroot的左子树或右子树和subroot相同)
- leetcode解题代码
class Solution {
public:
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
if (!subRoot) return true;
if (!root) return false;
return dfs(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot);
}
bool dfs(TreeNode* p, TreeNode* q){
if (!p && !q) return true;
else if (!p || !q) return false;
else if (p && q && p->val != q->val) return false;
return dfs(p->left, q->left) && dfs(p->right, q->right);
}
};
剑指offer.26
- 链接leetcode.cn/problems/sh…
- 解题方法:
辅助函数:判断B是否为A的左右子树为根节点的子树的子结构
主函数特判:空树不是任意数的子结构
如果A的根节点和B的根节点相同,判断辅助函数
返回值:B是否是A的左子树的子结构或A的右子树的子结构
- leetcode解题代码
class Solution {
public:
bool isSubStructure(TreeNode* A, TreeNode* B) {
if (!A && !B) return false;
if (!A || !B) return false;
if (A->val == B->val && dfs(A, B))
return true;
return isSubStructure(A->left, B) || isSubStructure(A->right, B);
}
bool dfs(TreeNode* p, TreeNode* q){
if (!p && !q) return false;
if (!p || !q) return false;
if (p->val == q->val)
return dfs(p->left, q->left) && dfs(p->right, q->right);
return false;
}
};
二叉树路径问题
问题分类
- 自顶向下:
就是从某一个节点(不一定是根节点),从上向下寻找路径,到某一个节点(不一定是叶节点)结束
- 非自顶向下:
就是从任意节点到任意节点的路径,不需要自顶向下
自顶向下
leetcode.257
class Solution {
public:
vector<string> res;
vector<string> binaryTreePaths(TreeNode* root) {
dfs(root, "");
return res;
}
void dfs(TreeNode* root, string path){
if (!root) return;
path += to_string(root->val);
if (!root->left && !root->right){
res.push_back(path);
return;
}
dfs(root->left, path+"->");
dfs(root->right, path+"->");
}
};
leetcode.113
- 链接leetcode.cn/problems/pa…
- 解题方法:
递归返回值:返回路径答案
递归终止条件:遍历到叶子节点且当前总和为0时将当前路径加入答案并返回
递归思路:左儿子存在递归左儿子,右儿子存在递归右儿子
- leetcode解题代码
class Solution {
public:
vector<vector<int>> res;
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<int> path;
dfs(root, targetSum, path);
return res;
}
void dfs(TreeNode* root, int sum, vector<int> path){
if (!root) return;
sum -= root->val;
path.push_back(root->val);
if (!root->left && !root->right && sum == 0){
res.push_back(path);
return;
}
dfs(root->left, sum, path);
dfs(root->right, sum, path);
}
};
leetcode.437
- 链接leetcode.cn/problems/pa…
- 解题方法:
递归返回值:返回路径数量
递归终止条件:遍历到总和为0时答案+1
递归思路:左儿子存在递归左儿子,右儿子存在递归右儿子,由于路径不需要从根节点开始,所以还需要以root的左右子树为起点进行递归
- leetcode解题代码
class Solution {
public:
int res = 0;
int pathSum(TreeNode* root, int targetSum) {
if (!root) return 0;
dfs(root, targetSum);
pathSum(root->left, targetSum);
pathSum(root->right, targetSum);
return res;
}
void dfs(TreeNode* root, long sum){
if (!root) return;
sum -= root->val;
if (sum == 0){
res ++;
}
dfs(root->left, sum);
dfs(root->right, sum);
}
};
非自顶向下
leetcode.124
- 链接leetcode.cn/problems/bi…
- 解题方法:
递归返回值:返回左右子树较长的路径+根节点值
递归终止条件:根节点不存在返回0
递归思路:左儿子存在递归左儿子,如果左儿子为负则取0;右儿子存在递归右儿子,如果右儿子为负则取0;更新答案
- leetcode解题代码
class Solution {
public:
int res = INT_MIN;
int maxPathSum(TreeNode* root) {
maxPath(root);
return res;
}
int maxPath(TreeNode* root){
if (!root) return 0;
int l = max(maxPath(root->left), 0);
int r = max(maxPath(root->right), 0);
res = max(res, l + r + root->val);
return max(l + root->val, r + root->val);
}
};
leetcode.687
- 链接leetcode.cn/problems/lo…
- 解题方法:
递归返回值:返回左右子树较长的路径
递归终止条件:根节点不存在返回0
递归思路:左儿子存在递归左儿子,如果左儿子的值和根节点值相同则l+1;右儿子存在递归右儿子,如果右儿子的值和根节点值相同则r+1;更新答案
- leetcode解题代码
class Solution {
public:
int res = INT_MIN
int longestUnivaluePath(TreeNode* root) {
if (!root) return 0
longestpath(root)
return res
}
int longestpath(TreeNode* root){
if (!root) return 0
int l = longestpath(root->left)
int r = longestpath(root->right)
if (root->left && root->val == root->left->val) l ++
else l = 0
if (root->right && root->val == root->right->val) r ++
else r = 0
res = max(res, l + r)
return max(l, r)
}
}
leetcode.543
- 链接leetcode.cn/problems/di…
- 解题方法:
递归返回值:返回左右子树较长的路径
递归终止条件:根节点不存在返回0
递归思路:递归左儿子,如果左儿子存在l+1否则l=0;递归右儿子,如果右儿子存在r+1否则r=0;更新答案
- leetcode解题代码
class Solution {
public:
int res = INT_MIN
int diameterOfBinaryTree(TreeNode* root) {
longestpath(root)
return res
}
int longestpath(TreeNode* root){
if (!root) return 0
int l = longestpath(root->left)
int r = longestpath(root->right)
if (root->left) l ++
else l = 0
if (root->right) r ++
else r = 0
res = max(res, l + r)
return max(l, r)
}
}