难度: 简单
题目描述
给你一棵二叉树的根节点 root ,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
示例
示例 1:

输入: root = [1,2,3,4,5]
输出: 3
解释: 3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
示例 2:
输入: root = [1,2]
输出: 1
提示:
-
树中节点数目在范围
[1, 10^4]内 -
-100 <= Node.val <= 100
解答 (JavaScript)
解题思路
这个问题的核心是找到树中任意两点间的最长路径。这条路径必然会有一个“拐点”或者说“最高点”,整条路径就是从这个“拐点”分别向其左、右子树延伸构成的。
我们的算法策略就是遍历树中的每一个节点,并假设它就是这个“拐点”,然后计算以它为“拐点”的路径长度,即 左子树深度 + 右子树深度。在所有节点上都计算一次,取其中的最大值即为整棵树的直径。
为了实现这一点,我们采用深度优先搜索(DFS)的后序遍历方式:
-
定义一个全局变量
maxDiameter来记录和更新目前找到的最大直径。 -
设计一个递归函数
dfs(node),它的核心任务有两个:-
计算和更新直径:在当前节点,利用其左右子树的深度(通过递归获得)计算穿过当前节点的路径长度 (
左深度 + 右深度),并用此值更新maxDiameter。 -
返回当前节点的深度:函数必须返回以当前节点为根的子树的深度 (
1 + Math.max(左深度, 右深度))。这个返回值是提供给其父节点,作为父节点计算的“原材料”。
-
通过这种方式,递归从下至上返回深度的同时,我们在每一层都完成了对直径的计算和更新,从而保证了最终结果的正确性。
代码实现
JavaScript
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {number}
*/
var diameterOfBinaryTree = function(root) {
// 用于记录和更新遍历过程中发现的最大直径。
let maxDiameter = 0;
/**
* 深度优先搜索辅助函数
* @param {TreeNode} node - 当前节点
* @returns {number} - 返回以当前节点为根的子树的最大深度(边数)
*/
const dfs = (node) => {
// 基线条件:如果节点为空,其深度为 0。
if (node === null) {
return 0;
}
// 后序遍历:先递归计算左、右子树的最大深度。
const leftDepth = dfs(node.left);
const rightDepth = dfs(node.right);
// 核心:计算穿过当前节点的路径长度,并更新全局最大直径。
// 路径长度 = 左子树深度 + 右子树深度。
maxDiameter = Math.max(maxDiameter, leftDepth + rightDepth);
// 核心:返回当前节点的深度,供其父节点使用。
// 节点深度 = 1 (当前节点到子节点的一条边) + 左右子树中较大的深度。
return 1 + Math.max(leftDepth, rightDepth);
};
// 从根节点开始深度优先搜索。
dfs(root);
// 遍历完成后,maxDiameter 即为所求的答案。
return maxDiameter;
};