[LeetCode] 124. 二叉树中的最大路径和(深度优先遍历)

本文深入探讨了二叉树中寻找最大路径和的算法实现,通过分解问题并运用记忆化搜索,降低了时间复杂度,实现了高效求解。文章详细解释了解题思路,并对比了不同解法,强调了模块化编程的重要性。

124. 二叉树中的最大路径和

给定一个非空二叉树,返回其最大路径和。

本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
在这里插入图片描述
解题思路: 本题题意是求解树中最大路径序列和(这里路径的定义是从一个节点到达另一个节点,不一定经过根节点),那么一条路径的形成是由节点、向左子树延展的路径、向右子树延展的路径组成,(这里延展的路径与我们常规理解的路径定义相同但不一定取整个直达叶子节点的全路径),那么想要题面定义的路径最长,只需左子树延展的路径和右子树延展路径均最长,就能使包含此节点的路径序列和最大,因此遍历过程中存在大量重复计算为降低时间复杂度,可以用记忆数组保存结果.

class Solution {
public:
    int maxPathVal(TreeNode* node) {
        if (mp.count(node)) return mp[node];
        if (!node) return 0;
        int res = node->val;
        int mx = max(maxPathVal(node->left), maxPathVal(node->right));
        res += max(0, mx);
        return mp[node] = res;
    }
    int helper(TreeNode* node) {
        if (!node) return 0;
        int mxleft = maxPathVal(node->left);
        int mxright = maxPathVal(node->right);
        return node->val + max(0, mxleft) + max(0, mxright);
    }
    void dfs(TreeNode* root) {
        if (!root) return;
        maxres = max(maxres, helper(root));
        dfs(root->left);
        dfs(root->right);
    }
    int maxPathSum(TreeNode* root) {
        dfs(root);
        return maxres;
    }
private:
    unordered_map<TreeNode*, int> mp;
    int maxres = INT_MIN;
};

看群友的解法,似乎增加一个函数解题,我新增了三个函数,在解此题时,我一直在想着如何将题目分解到我们平常已做过的题的解法,树的最大路径的题我们之前做过类似的有树的最长路径(无权值问题,根节点直达叶子节点问题),那么联想到此题,最自然的想法是,题目定义的路径一定是穿过某个节点的(简称节点A),那这条路径有三个延展方向,即左子树、右子树、父节点,而向上延展的父节点其实跟父节点向左右延展至子树的情况重复了,所以我们只需要考虑向左右子树延展的路径(定义1),那么我们可以先写一个求这个路径(定义1)最大值的函数,然后求题面路径时,只需要分别求左子树路径和右子树路径分别取正数然后与节点值加和即可。模块化编程,减少思维压力。

最后研究了一下群友的解题思路,发现我的解题思路和群友的解题是差不多的,群友的helper函数,本质上是求以访问节点为起点,向左子树和右子树延展能取得的最大路径,然后以根节点启动遍历,相当于对所有节点都遍历了一次,遍历的过程中记录题面定义的最大路径。定义一个求事件A的函数,在函数中也做一些其他的事,比如事件B,此类做法在算法题中可以这么干,但是在工程代码中,我觉得还是一个函数表达一件事,最好不要串扰,不然函数的行为很难追踪。

class Solution {
public:
    int helper(TreeNode* root) {
        if (!root) return 0;
        int left = max(0, helper(root->left));
        int right = max(0, helper(root->right));
        res = max(res, left + right + root->val);
        return max(left + root->val, right + root->val);
    }
    int maxPathSum(TreeNode* root) {
        helper(root);
        return res;
    }
private:
    int res = INT_MIN;
};

——————————
注:(定义1)粗体路径是指以该节点为起点且向左或者右子树延展的路径,与书本上路径定义大致相同,区别是不一定要到叶子节点。

写在后面

此题的收获是,解决一些陌生题时要学会将问题分解,分解到我们之前已经做过的题上,对应二叉树路径和的玩法,归结起来就一下几点:

  1. 求根节点到叶子节点的路径和(这是这类题的基础)
  2. 根节点到中间节点路径、中间节点到叶子节点路径,本质还是要点1
  3. 中间节点到中间节点,此题如果不转换,很容易误以为用图的遍历解决,但是树没有前向指针,这样会把问题弄复杂了。对于此问题要分解到要点1和要点2解题。本题属于这类问题。
<think>好,我现在需要帮用户整理LeetCode上几个二叉树相关问题的Python3答案。用户提到的题目包括中序遍历、最大深度、翻转二叉树、对称二叉树二叉树的直径层序遍历。我得一个一个来,先回忆每个题目的要求常用解法。 首先,中序遍历。这应该是基础题,要求按照左根右的顺序遍历。递归方法比较简单,但用户可能需要迭代法,因为有时候面试会要求。递归的话,定义一个辅助函数,先遍历左子树,然后访问根节点,再遍历右子树。迭代的话需要用栈,尽可能往左走,然后弹出栈顶,处理节点,再转向右子树。两种方法都要写出来。 接下来是最大深度。最大深度就是根节点到最远叶子节点路径长度。递归的话,左右子树的最大深度取较大者加一。也可以用BFS层序遍历,记录层数,每处理一层就加一,直到队列为。两种方法都行,但递归更简洁。 翻转二叉树,也就是每个节点的左右子树交换。可以用递归,先翻转左右子树,然后交换。或者用队列进行层序翻转,每次取出节点交换左右孩子,然后入队的孩子。递归应该更直接。 对称二叉树,判断是否镜像对称。可以用递归,比较左子树的左右子树的右,以及左子树的右右子树的左。或者用队列迭代,每次取出两个节点比较,然后按顺序入队它们的左右孩子。递归方法可能更直观。 二叉树的直径,直径是任意节点间最长路径的长度。这个路径可能不经过根节点,所以需要计算每个节点的左右子树高度之,并更新最大值。后序遍历,计算左右子树的高度,取较大者加一,同时维护一个全局变量记录最大直径。注意直径是边数,即节点数减一,所以计算左右高度之即可。 层序遍历,需要按层输出节点的值。用队列实现BFS,每次处理一层,记录当前层的节点数,循环处理每个节点,将其子节点入队,并将当前层的值存入结果列表。这题相对基础,但要注意处理每一层的节点数量。 现在要确保每个解法都是Python3的代码,符合题目要求,并且效率较高。递归方法可能会有栈溢出的问题,但对于LeetCode的测试用例通常没问题。迭代方法更安全一些。对于每个题目,需要给出两种解法吗?比如中序遍历的递归迭代,或者选一种典型的。用户可能需要多种解法,所以尽量提供两种,如果适用的话。例如,中序遍历的递归迭代,最大深度的递归BFS,翻转二叉树的递归迭代,对称二叉树的递归迭代,直径的后序遍历递归,层序遍历的BFS。 然后,检查每个解法的正确性。比如,翻转二叉树的递归解法是否正确交换左右子树。对称二叉树的递归条件是否正确,比较左左右右,左右右左。直径的计算是否在递归过程中更新最大值,初始值设为0是否正确。 另外,注意代码格式,使用正确的语法,比如在Python中类的定义,函数参数,缩进等。比如,中序遍历的递归辅助函数可能需要修改结果列表,或者将结果作为参数传递。不过通常用一个实例变量或者嵌套函数来捕获变量。比如在递归函数中,传入结果列表,然后修改它。 比如中序遍历的递归代码: def inorderTraversal(root): res = [] def helper(node): if not node: return helper(node.left) res.append(node.val) helper(node.right) helper(root) return res 这样是正确的。而迭代方法则需要用栈,循环处理左子树,然后弹出处理,再处理右子树。 最大深度的递归: def maxDepth(root): if not root: return 0 return 1 + max(maxDepth(root.left), maxDepth(root.right)) 层序遍历的BFS: def levelOrder(root): if not root: return [] res = [] queue = deque([root]) while queue: level = [] for _ in range(len(queue)): node = queue.popleft() level.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) res.append(level) return res 对称二叉树的递归解法: def isSymmetric(root): def check(left, right): if not left and not right: return True if not left or not right: return False return left.val == right.val and check(left.left, right.right) and check(left.right, right.left) return check(root, root) 注意这里传入根节点的左右子树,或者直接比较根的两个副本,比如根节点的左右。 二叉树的直径问题,需要一个全局变量记录最大值,然后在递归计算高度的同时更新这个最大值: class Solution: def diameterOfBinaryTree(self, root: TreeNode) -> int: self.max_diameter = 0 def depth(node): if not node: return 0 left = depth(node.left) right = depth(node.right) self.max_diameter = max(self.max_diameter, left + right) return 1 + max(left, right) depth(root) return self.max_diameter 这里每次递归返回的是当前节点的高度,而直径是左右高度之,所以在递归过程中更新最大值。注意这里的直径是边数,即路径长度为左高度+右高度,所以不需要减一。 翻转二叉树的递归解法: def invertTree(root): if not root: return None left = invertTree(root.left) right = invertTree(root.right) root.left, root.right = right, left return root 或者更简洁的写法: def invertTree(root): if root: root.left, root.right = invertTree(root.right), invertTree(root.left) return root 这两种写法都是正确的,但要注意Python中的执行顺序,右边的表达式先求值,所以交换是正确的。 总结每个问题的解法,确保代码正确,然后按照用户的要求,用中文解释每个解法的思路,给出代码示例,并注意格式中的数学表达式用$...$,独立公式用$$...$$,但可能在这题中用得比较少。不过用户的问题是关于代码的,所以可能数学表达式不多,可能涉及复杂度分析的时候需要。 最后,将每个问题的答案按用户列出的顺序整理,每个问题给出一种或两种解法,并简要说明思路注意点。确保结构清晰,步骤明确,帮助用户理解每个问题的解决方法。</think>以下是LeetCode二叉树常见问题的Python3解答及解析: --- ### 一、二叉树的中序遍历 **题目要求**:返回节点值的`左 -> 根 -> 右`顺序列表。 #### 1. 递归解法 ```python def inorderTraversal(root): res = [] def helper(node): if not node: return helper(node.left) res.append(node.val) helper(node.right) helper(root) return res ``` **时间复杂度**:$O(n)$,每个节点访问一次。 #### 2. 迭代解法(栈) ```python def inorderTraversal(root): res = [] stack = [] curr = root while curr or stack: while curr: stack.append(curr) curr = curr.left curr = stack.pop() res.append(curr.val) curr = curr.right return res ``` **核心思路**:利用栈模拟递归,优先访问左子树。 --- ### 二、二叉树最大深度 **题目要求**:返回从根节点到最远叶子节点的最长路径上的节点数。 #### 递归解法 ```python def maxDepth(root): if not root: return 0 return 1 + max(maxDepth(root.left), maxDepth(root.right)) ``` **时间复杂度**:$O(n)$,每个节点遍历一次。 --- ### 三、翻转二叉树 **题目要求**:交换每个节点的左右子树。 #### 递归解法 ```python def invertTree(root): if root: root.left, root.right = invertTree(root.right), invertTree(root.left) return root ``` **核心思路**:递归交换左右子树,直到叶子节点。 --- ### 四、对称二叉树 **题目要求**:判断二叉树是否镜像对称。 #### 递归解法 ```python def isSymmetric(root): def check(left, right): if not left and not right: return True if not left or not right: return False return left.val == right.val and check(left.left, right.right) and check(left.right, right.left) return check(root.left, root.right) if root else True ``` **核心思路**:比较左子树的左节点与右子树的右节点,反之亦然。 --- ### 五、二叉树的直径 **题目要求**:返回任意节点间最长路径的长度(边数)。 #### 后序遍历解法 ```python class Solution: def diameterOfBinaryTree(self, root): self.max_diameter = 0 def depth(node): if not node: return 0 left = depth(node.left) right = depth(node.right) self.max_diameter = max(self.max_diameter, left + right) return 1 + max(left, right) depth(root) return self.max_diameter ``` **核心思路**:对每个节点计算左右子树高度之,更新最大值。 --- ### 六、二叉树的层序遍历 **题目要求**:按层返回节点值的二维列表。 #### BFS队列解法 ```python from collections import deque def levelOrder(root): if not root: return [] res = [] queue = deque([root]) while queue: level_size = len(queue) current_level = [] for _ in range(level_size): node = queue.popleft() current_level.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) res.append(current_level) return res ``` **时间复杂度**:$O(n)$,每个节点入队出队一次。 --- ### 总结 1. **递归**是二叉树问题的常用解法,但需注意栈溢出风险。 2. **迭代**解法(如栈、队列)更适用于大数据量。 3. 大部分问题的时间复杂度为$O(n)$,间复杂度取决于递归深度或队列大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值