树的重心问题--邻接表+dfs搜索

目录

树和图的存储:

树的重心问题

注意事项:

实现代码--附有图的dfs搜索模板:


树和图的存储:

图的存储方式一般有两种-邻接数组和邻接表。

(1).邻接矩阵 二维数组  空间复杂度为n^2
(2).邻接表 n个点开n个单链表 每个点上的单链表存每个可以走到的点

树可以作为特殊的一种图,一般多采用邻接表的形式存储。而对于有向图和无向图的存储,只需要模拟出来有向图的存储结构,在需要存储无向图时添加两条权值相同的边即可。


树的重心问题

给定一颗树,树中包含 n 个结点(编号 1∼n)和n−1 条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式

第一行包含整数 n,表示树的结点数。

接下来 n−1 行,每行包含两个整数 a 和 b,表示点 a 和点 b 之间存在一条边。

输出格式

输出一个整数 m,表示将重心删除后,剩余各个连通块中点数的最大值。

数据范围

1≤n≤105

输入样例

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

输出样例:

4

注意事项:

有向图可以表示无向图 添加边的时候添加两条边即可
在DFS对于图遍历问题中 dfs很多时候不直接返回答案 而是返回当前节点经过DFS遍历后所有节点的值 再根据题目对需要的值进行迭代更新实现


实现代码--附有图的dfs搜索模板:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100010, M = 2*N;
//表示当前节点
int idx;
//表示当前的头节点对应的单链表
int h[N];
//表示每个单链表中每个节点对应的编号
int e[M];
//表示next指针
int ne[M];
//题目中的n个编号
int n;
//用来判断当前节点是否被使用
bool st[N];
//用于记录将重心删除后 剩余各个连通块中的数目最大值
int ans = N;

//向编号为a的元素的链表中插入b元素
void add(int a,int b){
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

//dfs模板
// void dfs(int u){
//     //表示当前点已经被搜过
//     st[u] = true;
//     for(int i=h[u];i!=-1;i=ne[i]){
//         int j = e[i];
//         if(!st[j]){
//             dfs(j);
//         }
//     }

// }


//返回以u为根的子树中节点的个数,包括u节点
int dfs(int u){
    //标记当前点已经被搜过 开始进行遍历
    st[u] = true;
    int res =0;//用于记录最大的连通块中点的个数
    int sum = 1;//用于记录子树中的节点树 包含当前根节点 所以初始为1
    for(int i=h[u];i!=-1;i=ne[i]){
        int j = e[i];//记录当前节点的子节点的编号
        //没有被访问过
        if(!st[j]){
            int s = dfs(j);
            res = max(s,res);
            sum+=s;
        }
    }
    //选择以u为节点最大的连通块中的节点数目 
    res = max(res,n-sum);//res表示u节点子节点部分中的连通块最大值 n-sum表示u节点上面的部分的节点值
    //选择连通块中最小的节点数目的节点作为中心
    ans = min(ans,res);

    return sum;

}

int main(){
    cin>>n;
    //初始化n个头节点的单链表数组
    memset(h,-1,sizeof(h));
    //一共有n个编号 有向边为n-1条
    for(int i=0;i<n-1;i++){
        int a,b;
        cin>>a>>b;
        //采用有向图的方式模拟无向图 需要添加两条边
        add(a,b),add(b,a);
    }
    //从编号1开始
    dfs(1);
    cout<<ans<<endl;

    return 0;

}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值