C++题解:树的直径

题目描述

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

输入样例

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 之间的路径,根据有无公共结点,可分为相交和不相交两种情况,如下图所示:
C++题解:树的直径_第1张图片

  1. 不相交的情况

因为树是连通的,所以从结点 t t t一定存在一条路径能够到达结点 y y y,如下图所示:
C++题解:树的直径_第2张图片
由于 u u u是到 t t t距离最远的点,所以 ① + ② ≥ ① + ⑤ + ④ ①+②\ge①+⑤+④ +++,那么 ② ≥ ⑤ + ④ ②\ge⑤+④ + ② + ⑤ ≥ ④ ②+⑤\ge④ +。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ +一定是树中最长的路径,而 ② + ⑤ + ③ ≥ ③ + ④ ②+⑤+③\ge③+④ +++,所以如结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。

  1. 相交的情况
    C++题解:树的直径_第3张图片
    由于 u u u是到 t t t距离最远的点,所以 ① + ② ≥ ① + ④ ①+②\ge①+④ ++,即 ② ≥ ④ ②\ge④ ,那么 ③ + ② ≥ ③ + ④ ③+②\ge③+④ ++。我们假设 x y xy xy是树的一条直径,即 ③ + ④ ③+④ +一定是树中最长的路径,如果结点 u u u不是某条直径的端点,那么树中一定存在一条大于等于当前直径的路径,与假设矛盾。

证毕。

算法实现

使用邻接表实现一棵无根树,在树中任取一个结点t

  1. 通过DFS求出所有点it的距离dep[i]
  2. 打擂台,求出到t距离最远的结点u
  3. 使用相同的方法求出到u距离最远的结点v
  4. 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;
}

你可能感兴趣的:(树,C++算法及题解,c++,算法,图论)