二叉树最大直径算法疑问:递归返回值与高度计算逻辑困惑
首先先修正一下原代码的小问题(补全类定义,修正易混淆的变量名),方便后续理解:
class TreeNode: def __init__(self, val, left=None, right=None): self.val = val self.left, self.right = left, right class TreeDiameter: def __init__(self): self.treeDiameter = 0 def find_diameter(self, root): self.calculate_height(root) return self.treeDiameter def calculate_height(self, currentNode): if currentNode is None: return 0 # 原变量名leftTreeDiameter容易误导,实际存储的是左子树高度 left_tree_height = self.calculate_height(currentNode.left) right_tree_height = self.calculate_height(currentNode.right) # 计算当前节点作为根的直径,更新全局最大直径 current_diameter = left_tree_height + right_tree_height + 1 self.treeDiameter = max(self.treeDiameter, current_diameter) # 返回当前节点所在子树的高度 return max(left_tree_height, right_tree_height) + 1
接下来逐个拆解你的疑问:
问题1:calculate_height最后一行return max(left_tree_height, right_tree_height) + 1的作用是什么?为何需要这样返回?
这个返回语句的核心是计算并返回当前节点所在子树的高度,是递归链条里的关键“信息传递”环节。
递归的逻辑是自底向上的:每个节点的高度,等于它左右子树中更高的那个子树的高度,再加上自身这一层(+1)。比如叶子节点的左右子树高度都是0,所以叶子节点的高度是1;如果一个节点左子树高度是2,右子树高度是3,那它的高度就是3+1=4。
为什么必须返回这个值?因为上层节点在计算自己的直径时,需要知道左右子树的最大高度(直径=左子树高度+右子树高度+1)。如果不返回这个高度,上层节点就没法完成自己的直径计算,整个递归链条就断了。
问题2:为何可以用left_tree_height = self.calculate_height(currentNode.left)获取左子树高度?我原以为需通过带参数累加的递归方法计算,且要分别处理左右子树,我的逻辑误区在哪里?
你的误区是把递归的状态传递想复杂了~递归本身会通过函数调用栈帮你保存每一层的计算状态,完全不需要额外的累加参数。
calculate_height(currentNode.left)这个调用,本质是让函数完整遍历并计算左子树的所有节点:从左子树的根节点开始,一步步递归到最底层的叶子节点,然后自底向上算出整个左子树的高度,最后把结果返回给上层调用。
举个简单例子:如果左子树是一个叶子节点,调用calculate_height时,会先检查它的左右孩子都是None,返回0;然后叶子节点自己计算max(0,0)+1=1,这个1就是叶子节点所在子树的高度,也就是整个左子树的高度,直接返回给父节点。
你之前想的“带参数累加”是自顶向下的递归思路,但这个解法用的是自底向上的方式:每个节点的高度由子节点的高度推导而来,递归调用的返回值直接就是子树高度,不需要额外传参数记录当前层级。
问题3:如何理解该递归方法的栈跟踪过程?
我们用一棵简单的二叉树来模拟完整的栈执行流程,比如这棵树:
1 / \ 2 3
假设treeDiameter初始值为0,调用find_diameter(root)后,会触发calculate_height(1),栈的变化如下:
- 调用
calculate_height(1),压入栈 → 栈内容:[calculate_height(1)] - 执行
left_tree_height = calculate_height(2),调用calculate_height(2),压入栈 → 栈内容:[calculate_height(1), calculate_height(2)] - 调用
calculate_height(2.left)(2的左孩子是None),返回0,栈弹出该调用 → 2的左子树高度为0 - 调用
calculate_height(2.right)(2的右孩子是None),返回0,栈弹出该调用 → 2的右子树高度为0 - 计算2的直径:
0+0+1=1,更新treeDiameter为1 - 返回
max(0,0)+1=1给上层,栈弹出calculate_height(2)→ 1的左子树高度为1 - 执行
right_tree_height = calculate_height(3),调用calculate_height(3),压入栈 → 栈内容:[calculate_height(1), calculate_height(3)] - 同理,3的左右孩子都是None,两次调用
calculate_height(None)都返回0,栈弹出这两个调用 - 计算3的直径:
0+0+1=1,treeDiameter保持max(1,1)=1 - 返回
max(0,0)+1=1给上层,栈弹出calculate_height(3)→ 1的右子树高度为1 - 计算1的直径:
1+1+1=3,更新treeDiameter为3 - 返回
max(1,1)+1=2给find_diameter,栈弹出calculate_height(1) find_diameter返回treeDiameter=3,即这棵树的最大直径
整个过程中,栈的压入是“向下深入到树的叶子节点”,弹出是“从叶子节点向上计算高度和直径”,每一步都在传递子树高度,同时更新全局的最大直径。
内容的提问来源于stack exchange,提问作者Michael Torres

