543. 二叉树的直径#
生命的横跨:寻找树中的最大距离#
想象你站在一片茂密的森林中,试图找出从一个极端到另一个极端最长能走多远。在二叉树的世界里,直径就是这样一种量度 —— 它不仅仅是一个数字,更是树的生命力和广度的见证。
问题描述#
题目目标#
给定一棵二叉树,你需要计算它的直径长度。二叉树的直径是任意两个节点路径长度中的最大值,这条路径可能经过也可能不经过根节点。这里的路径长度指两节点之间边的数目。
示例 1#
输入: root = [1,2,3,4,5]
输出: 3
二叉树示意:
1
/ \
2 3
/ \
4 5text说明: 最长路径可以是 4 -> 2 -> 1 -> 3 或 5 -> 2 -> 1 -> 3,一共经过 3 条边,所以答案是 3。
补充说明#
- 二叉树通常使用层序数组表示,
null表示该位置没有节点。
直径的本质:不仅仅是长度#
二叉树的直径(LeetCode第543题)是树中任意两个节点之间最长路径的长度。这个定义乍看可能简单,但蕴含着深刻的算法智慧。关键在于:
- 路径可以不经过根节点
- 长度定义为路径经过的边的数量
- 需要遍历整棵树才能找到最长路径
解题思路:寻径之旅的递归哲学#
解决这个问题的核心是理解:树的直径 = 左子树深度 + 右子树深度
public class Solution {
// 全局变量:记录遍历过程中发现的最大直径(按边数计算)
private int maxDiameter = 0;
public int diameterOfBinaryTree(TreeNode root) {
// 一次 DFS:在计算每个节点深度的同时更新最大直径
calculateDepth(root);
// 返回最大直径(边的条数,不是节点个数)
return maxDiameter;
}
private int calculateDepth(TreeNode node) {
// 递归终止条件:空节点深度为 0
if (node == null) return 0;
// 递归获取左子树深度(节点数)
int leftDepth = calculateDepth(node.left);
// 递归获取右子树深度(节点数)
int rightDepth = calculateDepth(node.right);
// 经过当前节点的路径长度(边数)= 左深度 + 右深度
// 与历史最大值比较并更新全局答案
maxDiameter = Math.max(maxDiameter, leftDepth + rightDepth);
// 返回当前节点向下的最大深度:取左右较大者并加上当前节点
return Math.max(leftDepth, rightDepth) + 1;
}
}java代码的深层逻辑解析#
-
maxDiameter全局变量- 记录树的最大直径
- 在遍历过程中不断更新
-
calculateDepth()方法- 同时完成两个任务: a. 计算节点深度 b. 更新最大直径
-
直径计算的关键:
leftDepth + rightDepth- 代表穿过当前节点的最长路径
- 不一定经过根节点
迭代方法:显式深度遍历#
public int diameterOfBinaryTree(TreeNode root) {
// 空树没有路径,直径为 0
if (root == null) return 0;
// 记录当前已知最大直径
int maxDiameter = 0;
// 节点栈:用于手动遍历整棵树
Stack<TreeNode> nodeStack = new Stack<>();
// 深度栈:与节点栈一一对应,保存节点所在深度(此实现中主要用于同步遍历状态)
Stack<Integer> depthStack = new Stack<>();
// 根节点入栈,深度记为 0
nodeStack.push(root);
depthStack.push(0);
// 遍历所有节点,尝试把每个节点当作“直径拐点”
while (!nodeStack.isEmpty()) {
// 弹出当前节点及其深度信息
TreeNode node = nodeStack.pop();
int currentDepth = depthStack.pop();
// 计算左子树深度(为空则记 0)
int leftDepth = node.left != null ?
getNodeDepth(node.left) : 0;
// 计算右子树深度(为空则记 0)
int rightDepth = node.right != null ?
getNodeDepth(node.right) : 0;
// 用“左深度 + 右深度”更新最大直径
maxDiameter = Math.max(maxDiameter, leftDepth + rightDepth);
// 右子节点存在则入栈,待后续继续计算
if (node.right != null) {
nodeStack.push(node.right);
depthStack.push(currentDepth + 1);
}
// 左子节点存在则入栈,待后续继续计算
if (node.left != null) {
nodeStack.push(node.left);
depthStack.push(currentDepth + 1);
}
}
// 返回遍历结束后得到的最大直径
return maxDiameter;
}
// 辅助方法:获取节点深度
private int getNodeDepth(TreeNode node) {
// 空节点深度为 0
if (node == null) return 0;
// 当前节点深度 = 1 + max(左深度, 右深度)
return 1 + Math.max(
getNodeDepth(node.left),
getNodeDepth(node.right)
);
}java性能分析:算法的呼吸#
时间复杂度:O(n)#
- 每个节点仅访问常数次
- n为树中节点总数
空间复杂度:O(h)#
- h为树的高度
- 最坏情况可达O(n)
- 最好情况(平衡树)为O(log n)
深度思考:直径背后的数学之美#
- 为什么深度和直径如此紧密相连?
- 如何理解树的”横跨”概念?
- 递归如何帮助我们解决复杂的遍历问题?
实际应用场景#
- 网络拓扑分析
- 社交网络中的影响力研究
- 生物进化树的直径研究
- 组织结构的层级分析
递归的诗:寻径的艺术#
二叉树直径不仅仅是一道算法题,更是递归思想的诗意展现。它教会我们:
- 复杂问题可以通过重复的简单逻辑解决
- 全局性质可以通过局部遍历逐步揭示
- 代码的优雅源于对问题本质的深刻理解
记住,探索二叉树的直径就像穿越知识的森林,重要的是保持好奇和系统的思考!