1. 简介
树中所有简单路径的最大值即为树的直径,可以通过两次 DFS 或者树形 DP 在 O(n) 时间内求解。
2. 思路
2.1 两次 DFS
- 
首先将任意一个结点 u 看作是树根,然后进行 DFS 求出最远的结点 s;则 s 一定是树的直径的其中一个端点。
 
证明
  假设结点 s′ 和 t′ 之间唯一的简单路径为树的直径,(a,b) 表示结点 a 和 b 之间的唯一的简单路径的长度。若该最远点 s 不是树的直径的一个端点,则对于当前根结点 u 来说:
- 如果 s′ 或 t′ 二者有一个不与 s 在同一个子树(假设为 t′),则由于
 
(s′,t′)≤(s′,u)+(u,t′)≤(s,u)+(u,t′)=(s,t′)
与 (s′,t′) 为直径而 s 不是直径的端点矛盾。
- 如果 s′、t′ 均与 s 在同一个子树,易知树的直径 (s′,t′) 没有经过树根 u。设该子树根为 u′,则易知 u′=s′ 且 u′=t′ 且 u′=s,于是 (u′,s) 一定为子树 u′ 中的最大值,即 s 为子树 u′ 的最远点,对子树 u′ 同样进行上述分析,以此类推。
 
综上可知,s 一定是树的直径的一个端点。
推论:树中任意结点的最远点要么是 s,要么是 t((s,t) 为树的直径)。
  证明同上,略。
- 然后将 v 看作是树根,再进行一次 DFS 求出最远点 t,则 s 与 t 之间的简单路径长度即为树的直径。
 
2.2 树形 DP
- 
定义树的高度为树根到所有结点的简单路径的最大值,次高度为树根到所有结点的简单路径第二大的值(高度 ≥ 次高度)。
 
- 
对于树中任意一个结点 v,若 v 到其子树 li 中的最远结点的距离为 Li,设所有 Li 中最大的二者为 L1rt=hi+(li,v) 和 L2nd=hj+(lj,v),对应的 v 的子树为 li 和 lj(没有子树 = 子树对应的 Li 为零,即没有足够的子树可以看作有 Li 为零的对应子树),则子树 v 中过结点 v 的最长简单路径等于
 
L1rt+L2nd
子树 v 的高度等于
max{Lk=hk+lk}
其中,k 取遍 v 的所有子树,L1rt 和 L2nd 即对应子树 v 的高度和次高度。
- 
由于树的直径一定是以树中某个结点 v′ 为根的子树中过结点 v′ 的最长简单路径,因此计算每个树结点 u′ 的过结点 u′ 的最长简单路径,同时维护一下最大值即可。
 
- 
计算每个树结点 u′ 的过结点 u′ 的最长简单路径就是一个树形 dp 问题:以当前结点为根的子树的高度和次高度的解取决于其子树的高度,而需要计算的最长简单路径即为 u′ 子树的高度和次高度之和。
 
3. 代码
【注】以下模板均是基于无权树求树的直径,有权树只需在前向星中记录一下边权,然后更新结点高度/深度不是 + 1 而是 + 对应边权即可。
3.1 两次 DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
   | #include <bits/stdc++.h> using namespace std;
  #ifndef _DIAMETEROFTREE_ #define _DIAMETEROFTREE_ #define ll int  #define MAXN 10005
 
  struct DiameterOfTree{           ll cnt;              ll head[MAXN];       struct Edge{         ll to;               ll next;         }e[MAXN<<1];              void init() {         cnt = 0;         memset(head, -1, sizeof(head));     }          void addEdge(ll u, ll v) {                  e[cnt].next = head[u];         e[cnt].to = v;         head[u] = cnt++;         e[cnt].next = head[v];         e[cnt].to = u;         head[v] = cnt++;     }          ll ans;              ll vis[MAXN];        ll depth[MAXN];      void dfs(ll u) {         vis[u] = 1;         for(ll i = head[u]; i != -1; i = e[i].next) {             ll v = e[i].to;             if(!vis[v]) {                 depth[v] = depth[u] + 1;                 if(depth[v] > depth[ans]) {                     ans = v;                 }                 dfs(v);             }         }     }          ll getDiameter(ll u) {                  ans = u;         memset(vis, 0, sizeof(vis));         memset(depth, 0, sizeof(depth));         dfs(u);                  memset(vis, 0, sizeof(vis));         memset(depth, 0, sizeof(depth));         dfs(ans);         return depth[ans];     } }; #endif
 
  | 
 
3.2 树形 dp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
   | #include <bits/stdc++.h> using namespace std;
  #ifndef _DIAMETEROFTREE_ #define _DIAMETEROFTREE_ #define ll int  #define MAXN 10005
 
  struct DiameterOfTree{          ll cnt;              ll head[MAXN];       struct Edge{         ll to;               ll next;         }e[MAXN<<1];              void init() {         cnt = 0;         memset(head, -1, sizeof(head));     }          void addEdge(ll u, ll v) {                  e[cnt].next = head[u];         e[cnt].to = v;         head[u] = cnt++;         e[cnt].next = head[v];         e[cnt].to = u;         head[v] = cnt++;     }          ll diameter;             ll vis[MAXN];            ll dfs(ll u) {         vis[u] = 1;         ll h1 = 0, h2 = 0;         for(ll i = head[u]; i != -1; i = e[i].next) {             ll v = e[i].to;             if(!vis[v]) {                 ll h = dfs(v) + 1;                 if(h > h1) {                     h2 = h1;                     h1 = h;                 } else if(h > h2) {                     h2 = h;                 }             }         }         diameter = max(diameter, h1+h2);         return h1;     }          ll getDiameter(ll u) {         diameter = 0;         memset(vis, 0, sizeof(vis));         dfs(u);         return diameter;     } }; #endif
 
  |