树和图的存储
树是一种没有闭环的无向图,无向图是一种特殊的有向图。因此我们只要表示出有向图就可以了。有向图的表示有两种,分别是邻接矩阵和邻接表。用的比较多的是邻接表。
邻接表的结构就是一个数组拉一个链表
数组建立邻接表
//邻接表
int h[N], e[N * 2], ne[N * 2], idx;
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
树的bfs模板
邻接表:
1 -> 2
2 -> 1 -> 3
3 -> 2 -> 4 - > 6
4 ->3 -> 5
5 -> 4
6 -> 3 -> 7
7 -> 6
以第u个节点为起点进行深搜:(例如节点1)
先搜索与他相连的第一个节点一路到底,搜索完了再回溯搜与之相连的第二个节点,本图中与1向量的只有2,因此搜索以2为节点的。
// 需要标记数组st[N]
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);
}
}
}
例题:树的重心
题目描述
给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数n,表示树的结点数。
接下来n-1行,每行包含两个整数a和b,表示点a和点b之间存在一条边。
输出格式
输出一个整数m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围
1≤n≤10^5
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
思路:可以用树的深度优先搜索,对于每个节点求出去掉他后剩余各个连通块中点数的最大值,例如去掉4后下面的两个连通块分别是以3和6为根的子树中节点的数量,然后顶上那个是 n - size3 - size 6 - 1
int dfs(int u) ; 求出以u为根的树中节点的数量,所以在求第去掉第i个节点后所有连通块的最大值就可以用max(dfs(与u直接相连的节点), n - dfs(与u直接相连的节点) - 1 )
代码
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5 + 10,M = N * 2;
int idx,h[N],e[M],ne[M];
bool st[N]; //存储哪些点被遍历过了
int n;
int ans = N; //全局答案,最小的最大值
void add(int a,int b);
int dfs(int u);
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);
cout<<ans<<endl;
return 0;
}
void add(int a,int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
//树和图的深度优先搜索
//返回以u为根的子树中节点的数量
int dfs(int u){
int sum = 1; //当前树的大小
int res = 0; //当前点删除后每个子树的最大值
st[u] = true; //标记一下,已经被搜过了
for(int i = h[u];i != -1 ; i = ne[i]){
int j = e[i];
if(!st[j]){
int t = dfs(j);
res = max(res,t);
sum += t;
}
}
res = max(n - sum,res);
ans = min(ans,res);
return sum;
}