树中两点间的不重复经过的边和点道路称为两点的路径,路径的长度(路径上所经边的长度和)称为两点的距离。圆的直径是一个圆的最长的一条弦,而树的直径是树中两点间最长的路径。通常用一个无序点对(x,y)表示一棵树的直径。
现在输入一个有n个结点的树,结点编号为1到n,假设结点1为根。试求树的直径。
输入格式
输入包含n+1行,第一行为1个正整数n,表示树的结点个数。
接下来n-1行,每行两个整数x和y,表示结点x和y之间有一条边,其中x是y的父亲。
输出格式
输出只有一个整数,为树的直径。
数据范围
n ≤ 1 0 5 n≤10^5 n≤105
输入样例
6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7
输出样例
22
在树中任取一个结点 t t t,求到 t t t距离最远的结点 u u u,接着求到 u u u距离最远的结点 v v v,那么结点 u u u和 v v v之间的路径就是树的一条直径。
根据上述思想,只需证明第一次找到的结点 u u u一定是某条直径的端点即可。
反证法:假设距离 t t t最远的结点 u u u不是某条直径的端点,并且存在某条直径,其端点分别为 x x x、 y y y。那么 t t t到 u u u之间的路径和 x x x到 y y y 之间的路径,根据有无公共结点,可分为相交和不相交两种情况,如下图所示:
因为树是连通的,所以从结点 t t t一定存在一条路径能够到达结点 y y y,如下图所示:
由于 u u u是到 t t t距离最远的点,所以 ① + ② ≥ ① + ⑤ + ④ ①+②\ge①+⑤+④ ①+②≥①+⑤+④,那么 ② ≥ ⑤ + ④ ②\ge⑤+④ ②≥⑤+④, ② + ⑤ ≥ ④ ②+⑤\ge④ ②+⑤≥④。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ ③+④一定是树中最长的路径,而 ② + ⑤ + ③ ≥ ③ + ④ ②+⑤+③\ge③+④ ②+⑤+③≥③+④,所以如结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。
证毕。
使用邻接表实现一棵无根树,在树中任取一个结点t
:
i
到t
的距离dep[i]
t
距离最远的结点u
u
距离最远的结点v
dep[v]
就是树的直径需要对所有节点遍历一次,时间复杂度为 O ( n ) O(n) O(n)
#include
#include
using namespace std;
const int N = 100010, M = N * 2;
int h[N], e[M], ne[M], idx;
int n, dep[N];
//邻接表,向链表头部插入新结点
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
//求以u为根的子树中所有结点到根结点的深度
int dfs(int u, int fa, int depth)
{
dep[u] = depth;
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
dfs(v, u, depth + 1);
}
}
int main()
{
cin >> n;
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
//需要处理为无根树
add(a, b), add(b, a);
}
//任选一点u作为根结点,求所有点到u的距离
int u = 1;
dfs(u, 0, 0);
//求距离最远的点u
for(int i = 1; i <= n; i ++)
{
if(dep[i] > dep[u])
{
u = i;
}
}
//以u作为根结点,求所有点到u的距离
dfs(u, 0, 0);
//求距离最远的点u
for(int i = 1; i <= n; i ++)
{
if(dep[i] > dep[u])
{
u = i;
}
}
cout << dep[u] << endl;
return 0;
}
#include
#include
using namespace std;
const int N = 100010, M = 2 * N;
int h[N], e[M], ne[M], w[M], idx;
int n, ans;
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
//求以u为根的子树的最大深度
int dfs(int u, int father)
{
int d1 = 0, d2 = 0; //d1表示最大深度,d2表示次大深度
for(int i = h[u]; ~i; i = ne[i])
{
int v = e[i];
if(v == father) continue;
int d = dfs(v, u) + 1;
if(d >= d1) d2 = d1, d1 = d;
else if(d > d2) d2 = d;
}
ans = max(ans, d1 + d2);
return d1;
}
int main()
{
cin >> n;
//初始化邻接表头指针
memset(h, -1, sizeof h);
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
//实现无根树,每条边保存两次
add(a, b), add(b, a);
}
dfs(1, -1);
cout << ans << endl;
return 0;
}