其他分享
首页 > 其他分享> > 树的直径

树的直径

作者:互联网

树的直径

题目描述

树中两点间的不重复经过的边和点道路称为两点的路径,路径的长度(路径上所经边的长度和)称为两点的距离。圆的直径是一个圆的最长的一条弦,而树的直径是树中两点间最长的路径。通常用一个无序点对(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≤10
5

输入样例

6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7

输出样例

22

算法思想

在树中任取一个结点 t,求到 t距离最远的结点 u,接着求到 u距离最远的结点 v,那么结点 u 和 v 之间的路径就是树的一条直径。证明
根据上述思想,只需证明第一次找到的结点 u 一定是某条直径的端点即可。

反证法:假设距离 t最远的结点 u不是某条直径的端点,并且存在某条直径,其端点分别为 x 、 y 。那么 t 到 u 之间的路径和 x 到 y 之间的路径,根据有无公共结点,可分为相交和不相交两种情况,如下图所示:

不相交的情况
image
因为树是连通的,所以从结点 t t t一定存在一条路径能够到达结点 y y y,如下图所示:
image

由于 u u u是到 t t t距离最远的点,所以 ① + ② ≥ ① + ⑤ + ④ ①+②ge①+⑤+④ ①+②≥①+⑤+④,那么 ② ≥ ⑤ + ④ ②ge⑤+④ ②≥⑤+④, ② + ⑤ ≥ ④ ②+⑤ge④ ②+⑤≥④。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ ③+④一定是树中最长的路径,而 ② + ⑤ + ③ ≥ ③ + ④ ②+⑤+③ge③+④ ②+⑤+③≥③+④,所以如结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。

相交的情况
image

由于 u 是到 t 距离最远的点,所以 ① + ② ≥ ① + ④ ①+②ge①+④ ①+②≥①+④,即 ② ≥ ④ ②ge④ ②≥④,那么 ③ + ② ≥ ③ + ④ ③+②ge③+④ ③+②≥③+④。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ ③+④一定是树中最长的路径,如果结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。
证毕。

算法实现

使用邻接表实现一棵无根树,在树中任取一个结点t:
通过DFS求出所有点i到t的距离dep[i]
打擂台,求出到t距离最远的结点u
使用相同的方法求出到u距离最远的结点v
dep[v]就是树的直径时间复杂度
需要对所有节点遍历一次,时间复杂度为 O ( n ) 代码实现一(简单模拟)

#include <iostream>
#include <cstring>
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 <iostream>
#include <cstring>
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;
}

标签:结点,idx,int,路径,ge,直径
来源: https://www.cnblogs.com/aska00/p/16524739.html