543. 二叉树的直径
!abstract 一句话本质 直径 = 任意节点处「左子树最深链 + 右子树最深链」的最大值,不一定经过根。把每个节点都当一次"拐点"算一遍取全局最大。
!quote 链接
题目
给定二叉树根 root,求其直径长度(任意两节点之间路径的边数最大值)。
思路(从暴力到最优)
暴力想法:枚举所有节点对,跑 LCA 算距离。O(n²) 起步,太慢。
关键观察:任意一条最长路径,一定存在一个"最高点"(路径上离根最近的节点,记为拐点)。这条路径长度 = 拐点的「左子树高度」+「右子树高度」。
所以:对每个节点都算一次"以它为拐点时的路径长度" = 左深度 + 右深度,取全局最大即为直径。
DFS 设计:
- 递归函数返回值:以当前节点为端点、向下延伸到叶子的最长链长度(边数)。
- 递归过程中副作用:用当前节点的
lLen + rLen更新全局ans。
关键卡点
!warning 我当时卡在这
- 直径不一定经过根! 完全可能藏在某个子树内部(左子树某条链最长 + 右子树某条链最长,拐点在子树里)。所以
ans必须在每个节点都更新一次,而不是只在根处算一次。- 返回值和 ans 是两回事:返回值是"向上交给父节点的那条最长链"(要 +1 算上到父的边);ans 更新的是"经过当前节点的路径长度"(不往上走,不加 1)。+1 的 1 代表"当前节点到父节点的那条边"。
- 单节点树直径是 0 :路径长度按边数算,需要 2 个节点 1 条边才有直径。代码中
root === null返回 0 保证叶子节点lLen = rLen = 0,更新ans = 0,不会算成 1。
代码
javascript
var diameterOfBinaryTree = function(root) {
let ans = 0;
function dfs(root) {
if (root === null) {
return 0;
}
const lLen = dfs(root.left);
const rLen = dfs(root.right);
ans = Math.max(ans, lLen + rLen);
return Math.max(lLen, rLen) + 1;
}
dfs(root);
return ans;
};
复杂度
- 时间:O(n),每个节点访问一次。
- 空间:O(h),h 为树高,退化为链表时 O(n);平衡树 O(log n)。
一句话记忆点
!tip 下次看到类似题 求"树中任意两点的最长路径" → DFS 返回"以本节点为端点的最长链",过程中用
左深+右深更新全局答案。ans 全局更新,返回值只管往上交。
相似题 / 套用模板
- \[0104-二叉树的最大深度]:dfs 返回
Math.max(left, right) + 1的模板,本题在其基础上多了一个"全局 ans 更新"。 -
- 二叉树中的最大路径和:同套路,DFS 返回"以本节点为端点的最大单链和",过程用
左 + 右 + 当前更新全局 ans。
- 二叉树中的最大路径和:同套路,DFS 返回"以本节点为端点的最大单链和",过程用
- 124 后续题见题单(最大路径和
难)。