11.29自底向上 DFS
104. 二叉树的最大深度
思路:自顶向上的方法
因为回溯和深搜是绑定的,回溯的过程涉及到depth的增减,所以DFS要把depth作为参数传递下去,而自底向上不用。
class Solution {
public:
int maxDepth(TreeNode* root , int depth = 1) {//这里有参数depth其实是自顶向下的做法
if(!root) return 0;
if(root->left == root->right) return depth;
return max(maxDepth(root->left , depth + 1),maxDepth(root->right , depth + 1));
}
};
灵神的做法:不必多加参数depth传递下去,并不是要找特定的某一层depth == k
class Solution {
public:
int maxDepth(TreeNode* root ) {
if(!root) return 0;
return max(maxDepth(root->left),maxDepth(root->right)) + 1;
}
};
自顶向下:
class Solution {
int res = 0;
int dfs(TreeNode* node, int depth){
if(!node) return depth;
return max(dfs(node->left , depth + 1),dfs(node->right , depth + 1));
}
public:
int maxDepth(TreeNode* root) {
return dfs(root , 0);//这里root的depth一定是0而不是1
}
};
灵神版:
auto& dfs允许你在 lambda 表达式内部递归地调用dfs,而不需要额外的参数。这是解决递归问题时常用的技巧,特别是在需要在递归过程中保持某些状态或避免重复定义时。通过引用捕获自身,允许在 lambda 内部递归调用。这种方式简洁且高效,避免了显式的参数传递和对象复制。
class Solution {
public:
int maxDepth(TreeNode* root) {
int ans = 0;
auto dfs = [&](auto& dfs, TreeNode* node, int depth) -> void {
if (node == nullptr) {
return;
}
depth++;//dfs这里按值传递参数不需要手动回溯
ans = max(ans, depth);
dfs(dfs, node->left, depth);
dfs(dfs, node->right, depth);
//这里不需要再加depth --
};
dfs(dfs, root, 0);
return ans;
}
};
是否需要手动回溯
需要手动回溯的情况:
路径问题:在解决路径问题,如寻找所有路径、路径计数或路径上的特定条件时,通常需要在递归返回前手动撤销一些操作,以确保下一次递归调用能够从正确的状态开始。
状态修改:如果你在递归过程中修改了对象的状态(如数组元素、节点的标记等),而这些修改不是最终答案的一部分,那么在递归返回前需要将状态恢复到修改前的状态。
动态规划:在动态规划问题中,如果你使用递归树来避免重复计算,那么在递归调用结束后,可能需要恢复一些状态,以确保不影响其他分支的计算。
图的遍历:在图的遍历中,为了避免重复访问节点,通常需要在访问后手动标记节点,然后在递归返回前取消标记。
不需要手动回溯的情况:
树的深度优先搜索(DFS):在树的深度优先搜索中,通常不需要手动回溯,因为每次递归调用都是独立的,不会影响其他调用的状态。
分治算法:在分治算法中,子问题的解通常是独立的,不需要在递归返回后恢复任何状态。
简单的递归:对于简单的递归问题,如计算阶乘、斐波那契数列等,不需要手动回溯,因为每次递归调用都是自包含的,不依赖于外部状态。
按值传递参数:如果你的递归函数通过值传递参数,那么每次递归调用都会有参数的副本,不需要手动回溯,因为递归结束后,局部变量的生命周期就结束了。
函数返回值:如果递归函数的目的是计算并返回一个值,而不是修改外部状态,那么通常不需要手动回溯。
总结来说,是否需要手动回溯取决于你的算法是否依赖于外部状态的修改,以及这些状态是否需要在递归调用之间保持一致。如果递归过程中的修改是局部的,不影响其他递归调用,那么就不需要手动回溯。如果递归过程中的修改需要在递归调用之间共享或保持一致,那么可能需要手动回溯以确保状态的正确性。
111. 二叉树的最小深度
思路:注意和求最大深度相区别开
以下是错误代码:
class Solution{ public: int minDepth(TreeNode* root) { if(!root) return 0; int l_min = minDepth(root->left); int r_min = minDepth(root->right); return min(l_min , r_min) + 1; } };在求最小深度时,我们需要考虑树可能不是完全二叉树,可能只有一个左子树或一个右子树。在这种情况下,我们需要返回非空子树的深度加1,如样例{1 , 2},最小高度为2而不是1.
这种做法看似把问题拆分为求左子树最小高度、右子树最小高度,但是返回时不对。只有左右子树均存在的时候可以这样求。应该对叶子节点进行判断,考虑只有左/右子树的情况。
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) {
return 0; // 空树的最小深度为0
}
if (root->left == nullptr) {
return minDepth(root->right) + 1; // 只有右子树
}
if (root->right == nullptr) {
return minDepth(root->left) + 1; // 只有左子树
}
return min(minDepth(root->left), minDepth(root->right)) + 1; // 两个子树都存在
}
};
自顶向下:
class Solution{
public:
int minDepth(TreeNode* root) {
if(!root) return 0;
int res = INT_MAX;
auto dfs = [&](auto& dfs ,TreeNode* node, int depth)-> void{
//这一步完成了剪枝过程,如果depth + 1比目前res大,则直接返回,顺便进行了depth+1
if(!node || ++depth >= res) return;
//遇到叶子节点才进行更新答案,返回。
if(node->left == node->right) {
res = depth;
return;
}
dfs(dfs , node->left , depth );
dfs(dfs , node->right , depth );
};
dfs(dfs , root , 0);
return res;
}
};
965. 单值二叉树
思路:自底向上,调用自身
边界条件:
- 如果碰到叶子节点了,说明过程中都正确,返回true;(可以去掉)
- 空节点返回true
判断逻辑:
- 左子树存在时如果值不等于父节点值,返回false。右子树同理
class Solution {
public:
bool isUnivalTree(TreeNode* root) {
if(!root) return true;
if(root->left && root->left->val != root->val) return false;
if(root->right && root->right->val != root->val) return false;
return isUnivalTree(root->left) && isUnivalTree(root->right);
}
};
100. 相同的树
思路:遍历两颗二叉树,把值存到数组里即可,存值的时候如果遇到空节点,也要存个范围之外的值代替,如样例:
本题范围为
-10^4 <= Node.val <= 10^4, 遇空节点存1e5即可。
自顶向下的前序遍历方法:
class Solution {
void dfs(TreeNode* root , vector<int>& vec){
if(!root) {
vec.push_back(1e5);
return;
}
vec.push_back(root->val);
dfs(root->left , vec );
dfs(root->right , vec );
}
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
vector<int> res1;
vector<int> res2;
dfs(p ,res1), dfs(q , res2);
return res1 == res2 ;
}
};
考虑自底向上的方法:
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p || !q) return p == q; //同时为空才行
return p->val == q->val && isSameTree(p->left , q->left) && isSameTree(p->right , q->right);//自底向上
}
};
101. 对称二叉树
思路: 在上一题【100. 相同的树】的基础上稍加改动
判断两棵树相同的代码中,把左右稍改即可变为判断镜像是否相同。
return p->val == q->val && isSameTree(p->left, q->right) && isSameTree(p->right, q->left);本题已经说了根节点非空,判断根的左右子树是否镜像相同即可。
class Solution {
// 在【100. 相同的树】的基础上稍加改动
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p == nullptr || q == nullptr) {
return p == q;
}
return p->val == q->val && isSameTree(p->left, q->right) && isSameTree(p->right, q->left);
}
public:
bool isSymmetric(TreeNode* root) {
return isSameTree(root->left, root->right);
}
};
951. 翻转等价二叉树
思路:
递归,要么反转要么不反转,有一种情况满足就行,即:
不反转:左和左比,右和右比 或 反转:左和右比,右和左比,翻译如下:
bool ll = dfs(dfs , p->left , q->left); bool lr = dfs(dfs , p->left , q->right); bool rl = dfs(dfs , p->right , q->left); bool rr = dfs(dfs , p->right , q->right); return (ll && rr) || (lr && rl);
class Solution {
public:
bool flipEquiv(TreeNode* root1, TreeNode* root2) {
auto dfs = [&](auto&& dfs , TreeNode* p , TreeNode* q){
if(!p || !q) return p == q;
if(p->val != q->val ) return false;
bool ll = dfs(dfs , p->left , q->left);
bool lr = dfs(dfs , p->left , q->right);
bool rl = dfs(dfs , p->right , q->left);
bool rr = dfs(dfs , p->right , q->right);
return (ll && rr) || (lr && rl);
};
return dfs(dfs , root1 , root2);
}
};
1379. 找出克隆二叉树中的相同节点
思路:
我们直接在 getTargetCopy 内部调用它自己,执行递归(先序遍历):
如果 original 是空节点,返回空。
如果 original=target(注意这里比较的是节点,不是节点值),说明我们找到了对应的节点,返回 cloned。
否则递归 original 和 cloned 的左子树,如果返回值 leftRes 不为空,说明 target 在左子树中,返回 leftRes。
否则递归 original 和 cloned 的右子树,由于题目保证 target 一定在二叉树中,所以直接返回递归右子树的返回值。
我们比较的是节点而不是节点值(例如 C++ 比较的是地址),所以下面的代码也适用于树中有值相同节点的情况(本题的进阶问题)。
class Solution {
public:
TreeNode *getTargetCopy(TreeNode *original, TreeNode *cloned, TreeNode *target) {
if (original == nullptr || original == target) {
return cloned;
}
auto left_res = getTargetCopy(original->left, cloned->left, target);
if (left_res) {
return left_res; // 已经找到 target,无需递归右子树
}
//由于题目保证 target 一定在二叉树中,所以直接返回递归右子树的返回值。
return getTargetCopy(original->right, cloned->right, target);
}
};


浙公网安备 33010602011771号