二叉树的深度优先经典问题——二叉树经典算法题

100. 相同的树 - 力扣(LeetCode)

注意:递归的终止条件可以通过枚举来找规律

101. 对称二叉树 - 力扣(LeetCode)

这道题的核心思想是:首先是通过遍历  没有问题  那只有全部对称  才会返回true  如果不是的话  中途就返回false就好了  可以少去判断/

第二次解答的问题:你只需要去判断最小的循环的逻辑   至于   p->left 是否存在且等于q->right

这是最后return中递归才要用到的东西

在逻辑与编程中,! 表示逻辑非运算,也就是取反操作。
所以 !p || !q 的意思是:
!p - 非p,如果p为真,则!p为假;如果p为假,则!p为真
|| - 逻辑或运算符
!q - 非q,如果q为真,则!q为假;如果q为假,则!q为真
综合起来:
!p || !q 的运算过程是:
1. 首先计算!p,取p的逻辑非
2. 再计算!q,取q的逻辑非
3. 最后进行逻辑或运算,只要!p或者!q有一个为真,结果就为真
举个例子:
- 如果p为真,q为假,则!p为假,!q为真,!p || !q结果为真
- 如果p为假,q为真,则!p为真,!q为假,!p || !q结果为真 
- 如果p为真,q为真,则!p为假,!q为假,!p || !q结果为假
- 如果p为假,q为假,则!p为真,!q为真,!p || !q结果为真
所以!p || !q表示只要p和q有一个为假,整个表达式结果就为真。这在编程逻辑中是一种常见的组合方式。

617. 合并二叉树 - 力扣(LeetCode)

我的代码(错误):

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
      if(root1 == nullptr && root2 == nullptr){
          return -1;
      }
     else if(root1 ==nullptr && root2 != nullptr)
     return root2->val;
    }
    else if(root2 ==nullptr && root1 != nullptr)
     return root1->val;
    }
    else if(root1 !=nullptr && root2 != nullptr){
        val = root1->val+ root2->val;
    }
    return mergeTrees(root1->left,root2->left)&&mergeTrees(root1->right,root2->right);
};

在 mergeTrees 函数中,这一行代码:
auto merged = new TreeNode(t1->val + t2->val);
它的作用是:创建一个新的树节点merged,这个节点的值是t1和t2两个节点值的和。
因为mergeTrees函数是要把两棵二叉树合并的,在合并的时候,对应位置的两个节点的值需要相加,生成一个新的节点。
所以这里:
1. 使用new TreeNode(...)创建了一个新的节点merged
2. 节点的值是t1->val + t2->val,也就是两个节点值相加
3. 然后后面继续递归合并左右子树
4. 最后返回新建的merged根节点
这样就可以同时遍历两棵树,逐节点合并,最终生成一棵新树。
所以这个自动创建新节点的代码,是mergeTrees核心逻辑的一部分,用于根据两个节点生成合并后的新节点。它实现了两棵树逐节点合并的功能的基础。 

错误:

1. 返回值类型错误
mergeTrees 函数需要返回合并后新的二叉树的根节点 TreeNode*。
但是这个代码在 root1 和 root2 都为 nullptr 时返回了一个 int 类型的值-1,这是不正确的。
mergeTrees 的返回值必须是 TreeNode* 类型。
2. 递归调用缺失
mergeTrees 需要递归地合并两个二叉树,但是这个代码只有几个 if 判断,没有递归调用 mergeTrees 函数的逻辑。
3. 未生成新节点
递归合并两个子树后,需要使用两个子树的节点值生成一个新的合并后的节点。这个逻辑在代码中不存在。
4. 逻辑错误
else if 之间的逻辑关系写错,不应该先判断 root2 再判断 root1,这与题目要求不符。
5. 语法错误 
if else 语句缺少括号,val的声明缺少类型。递归调用参数错误。
综上,这个代码缺少正确的递归逻辑,没有生成新节点,返回值和逻辑关系都有问题,无法正确实现两棵树的合并。需要重新考虑递归调用和新节点创建的逻辑,以及修正语法错误,才能正确实现 mergeTrees 的功能。

路径专题:

257. 二叉树的所有路径 - 力扣(LeetCode)

C++11 中引入了 to_string 方法,可以将数字转换为字符串。to_string 定义在头文件<string>中。to_string 的主要特点包括:1. 将数值转换为字符串to_string 可以将整数、浮点数、双精度浮点数等数值类型转换为字符串:string s1 = to_string(10); // s1 为 "10"
string s2 = to_string(5.2); // s2 为 "5.2"

112. 路径总和 - 力扣(LeetCode)

为什么会想到用一个int vall=0来记录整体的值?(why not?)





@Claude #112
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
       int vall= 0;
       if(root!= nullptr){
         vall += root->val;
         if(root->left == nullptr && root->right ==nullptr&& vall==targetSum){
             return true;
         }
         else if(root->left == nullptr && root->right ==nullptr&&vall!=targetSum){
           return false;
         }       }
    return  hasPathSum(root->left,targetSum)||hasPathSum( root->right, targetSum);}
};请问该代码有什么问题

3 条回复


Claude应用  6 分钟前

该代码的主要问题在于:1. 在递归调用hasPathSum函数时,没有更新vall变量,vall变量在递归调用中需要持续累加当前节点的值。2. 在递归终止条件判断时,没有比较vall和targetSum,直接返回true/false,这在递归过程中是不正确的。3. 函数缺少对root为空指针的判断,如果root为空,应该直接返回false。可以这样修改:

cpp
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr) return false;
        
        int vall = root->val;
        if (root->left == nullptr && root->right == nullptr) {
            return vall == targetSum; 
        }
        
        return hasPathSum(root->left, targetSum - vall) || 
               hasPathSum(root->right, targetSum - vall);
    }
};

主要修改:1. 加入对root为空的判断2. 在递归调用时,减去当前节点的值,以达到累加的效果3. 在递归终止条件判断时,比较累加的vall和targetSum这样可以正确实现二叉树路径求和的逻辑。 (已编辑) 

xiong gavin  5 分钟前

@Claude 我的代码中  vall += root->val;就是实现累加的意图,请问为什么最终没有成功?

Claude应用  5 分钟前

您在代码中使用 vall += root->val 来累加路径上的节点值是没有问题的。但是在递归调用 hasPathSum 的时候,vall 参数的值并没有传递下去,导致递归调用中 vall 的值是错误的。具体来看,在这一行:

return  hasPathSum(root->left,targetSum)||hasPathSum( root->right, targetSum);

调用左右子节点的时候,传递的 targetSum 参数依然是原始的 targetSum,没有减去当前节点 root 的值。也就是说,递归调用中用到的 vall 值是不正确的。解决方法是在递归调用的时候,把 targetSum 减去当前节点的值,作为新的 targetSum 传入递归函数,表示路径总和需要减去当前节点的值:

return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);

这样在递归调用中,vall 就能逐步累加起来,最终判断是否等于 targetSum。所以关键在于递归调用的时候,需要把当前节点的值从 targetSum 中减去,否则 vall 的累加就无法实现。

有一个误区:return sum == root->val;这个语句 

您提到的return currSum == targetSum这行代码,是用于判断当前节点是否是叶子节点,并且路径和是否等于目标和的。这里涉及到几个基本语法点:1. return语句return语句用于从一个函数返回值。在这里我们用它来返回一个bool类型的值,表示是否找到了满足条件的路径。2. ==操作符这是比较运算符,用于比较两个值是否相等。currSum == targetSum表示比较当前路径和currSum和目标和targetSum是否相等。3. 如果相等,返回true,否则返回false。true和false是bool类型的两个值,true表示真,false表示假。这里的意思是:
- 如果当前节点是叶子节点(左右子节点都为空),那么检查当前路径和currSum是否等于目标和targetSum
- 如果相等,返回true表示找到了满足条件的路径
- 如果不等,返回false表示还没找到所以这行代码的作用就是:
当遍历到叶子节点时,检查当前路径和是否满足条件,如果满足就返回true表示找到了,不满足就返回false表示还未找到。整段代码的思路是通过递归遍历所有可能的路径,并且记录当前路径上的节点值之和currSum。当遍历到叶子节点时就检查当前路径和是否满足条件。如果任何一条路径满足条件,最终都会返回true。

226. 翻转二叉树 - 力扣(LeetCode)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return nullptr;
        }
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/invert-binary-tree/solutions/415160/fan-zhuan-er-cha-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

不会导致由于先让root->left指向right从而丢失left吗?为什么不需要一个中间变量来存储

不会导致丢失left,因为在执行root->left = right;之前,我们已经将left指针存储在了left变量中,所以即使现在将root->left指向了right,我们仍然可以通过left变量来访问原来的left节点。同理,我们也已经将right指针存储在了right变量中,所以在执行root->right = left;之后,我们仍然可以通过right变量来访问原来的right节点。

因此,不需要使用中间变量来存储left和right节点,直接交换指针即可。

需要注意的是,这里的指针交换是针对指针本身的交换,而不是交换指针所指向的对象。12

引用

|

|

|

需要注意的是,这里的指针交换是针对指针本身的交换,而不是交换指针所指向的对象 这句话怎么理解?

在C++中,指针是一种特殊的变量类型,它存储了一个内存地址,该地址指向另一个变量的位置。当我们使用指针时,我们可以通过指针来访问指向的变量。指针交换是指将两个指针变量的值互换,使得它们指向的内存地址也随之互换。但是需要注意的是,这里的指针交换是针对指针本身的交换,而不是交换指针所指向的对象。也就是说,交换指针变量的值并不会影响到指针所指向的对象,只是改变了指针变量本身的指向关系。12

引用

个人总结:首先 做二叉树的题目最重要的是你要知道你到底用什么遍历

针对二叉树的问题,解题之前一定要想清楚究竟是前中后序遍历,还是层序遍历。

二叉树解题的大忌就是自己稀里糊涂的过了(因为这道题相对简单),但是也不知道自己是怎么遍历的。

这也是造成了二叉树的题目“一看就会,一写就废”的原因。

针对翻转二叉树,我给出了一种递归,三种迭代(两种模拟深度优先遍历,一种层序遍历)的写法,都是之前我们讲过的写法,融汇贯通一下而已。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值