目录
树和图的存储:
图的存储方式一般有两种-邻接数组和邻接表。
(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;
}