题目地址:
https://leetcode.com/problems/tree-diameter/
给定一个有树性质的无向图,求其直径。直径的意思是图中距离最远的两个顶点之间的距离。
先用邻接表建图。接着从任意一个点 v v v出发对图进行DFS,找到离 v v v最远的顶点 u u u,然后再从 u u u出发对图进行DFS,求出离 u u u最远的顶点的距离即可。
算法正确性证明:
只需证明第一次DFS搜到的最远的点一定可以作为直径的一个端点即可。对图的顶点个数做归纳。如果图只有
1
1
1或
2
2
2个顶点结论显然成立。接下来假设图有
n
n
n个顶点,并且第一次DFS是从
v
v
v开始搜的。如果
v
v
v位于某条直径上,那么直径的一个端点显然就是离
v
v
v的最远的点之一,结论正确。如果
v
v
v不位于任何一条直径上,那么对于去掉
v
v
v后产生的若干连通分支,必然有一个完全包含直径,而且这个直径也是该连通块的直径。设
u
u
u是
v
v
v的在该连通块里的邻接点,那么这个连通块的顶点个数比原图小,由归纳假设,这个连通块的与
u
u
u最远的点
w
w
w可以作为这个连通块的一条直径的端点。而从
v
v
v开始DFS的时候,当走到了含
u
u
u的连通分支的时候,
w
w
w就会被搜到。下面证明,不会有别的顶点比
w
w
w离
v
v
v更远。首先
u
u
u所在的连通块不会有这样的点,因为这个连通块里离
v
v
v最远的点就是离
u
u
u最远的点,而由归纳假设
w
w
w就是离
u
u
u最远的点。如果别的连通块里有离
v
v
v更远的点,比如说是
z
z
z,那么这个点到
v
v
v再到
w
w
w形成的路径要比直径(设直径是为
w
→
w
′
w\to w'
w→w′)更长(这个是可以证明的,设
f
f
f是路径长度函数,则
f
(
z
,
w
)
=
f
(
z
,
v
)
+
f
(
v
,
w
)
>
2
f
(
v
,
w
)
≥
f
(
v
,
w
)
+
f
(
v
,
w
′
)
>
f
(
w
,
w
′
)
f(z,w)=f(z,v)+f(v,w)>2f(v,w)\ge f(v,w)+f(v,w')>f(w,w')
f(z,w)=f(z,v)+f(v,w)>2f(v,w)≥f(v,w)+f(v,w′)>f(w,w′)),这就矛盾了。所以
w
w
w是整个图离
v
v
v最远的点之一。证明完毕。
代码如下:
import java.util.*;
public class Solution {
public int treeDiameter(int[][] edges) {
Map<Integer, List<Integer>> graph = new HashMap<>();
for (int[] edge : edges) {
graph.putIfAbsent(edge[0], new ArrayList<>());
graph.putIfAbsent(edge[1], new ArrayList<>());
graph.get(edge[0]).add(edge[1]);
graph.get(edge[1]).add(edge[0]);
}
boolean[] visited = new boolean[edges.length + 1];
// dis是一个长度为2的数组,dis[0]存的是DFS距离第0层递归的cur最远的顶点编号,dis[1]存的是距离
int[] dis = {0, 0};
// 从0出发进行DFS,将dis更新为距离0最远的顶点编号和距离
dfs(0, 0, dis, graph, visited);
// 将visited重新标为false,再从dis[0]开始DFS
Arrays.fill(visited, false);
dfs(dis[0], 0, dis, graph, visited);
// 返回距离dis[0]最远的顶点的距离
return dis[1];
}
// 从cur出发,curDis指递归的深度,
// dis是一个长度为2的数组,dis[0]存的是距离第0层递归的cur最远的顶点编号,dis[1]存的是距离
private void dfs(int cur, int curDis, int[] dis, Map<Integer, List<Integer>> graph, boolean[] visited) {
visited[cur] = true;
// 如果找到了更深的顶点,则更新dis数组
if (curDis > dis[1]) {
dis[0] = cur;
dis[1] = curDis;
}
for (int next : graph.get(cur)) {
if (!visited[next]) {
// 将递归深度传入dfs函数
dfs(next, curDis + 1, dis, graph, visited);
}
}
}
}
时空复杂度 O ( n ) O(n) O(n)。