#include<iostream> using namespace std; int N = 1e5 + 10,M = N*2; //h表示链表头,e存储的是所有的边,ne表示所有的next指针是多少,相当与n个单链表 int h[N],e[M],ne[M],idx; //深度优先搜索和宽度优先搜索每个点只会遍历一次 bool st[N];//存一下那些点已经被遍历过了,就不要再遍历他了 void add(int a,int b){//a前插入b e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } //树和图的深度优先搜索的代码 时间复杂度是O(n + m),因为点数和边数呈线性关系 //u表示已经遍历到这个节点了 int dfs(int u){ st[u] = true;//首先先标记下当前这个点已经被搜索过了 //遍历下u的所有的初边 for(int i = h[u];i != -1;i = ne[i]){ // 存储当前结点对应图里边结点的编号是多少 int j = e[i]; //如果当前点没有做过的话,就一直搜,一条路走到黑 if(!st[j]) dfs(j); } } int main(){ //头结点指向-1,n给单链表的头结点指向-1 memset(h,-1,sizeof h); dfs(1);//从第一个点开始搜索 }
例题:
846. 树的重心
给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。
请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。
重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。
输入格式
第一行包含整数n,表示树的结点数。
接下来n-1行,每行包含两个整数a和b,表示点a和点b之间存在一条边。
输出格式
输出一个整数m,表示重心的所有的子树中最大的子树的结点数目。
数据范围
1≤n≤1051≤n≤105
输入样例
9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6
输出样例:
4
#include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 1e5 + 10,M = N*2; //h表示链表头,e存储的是所有的边,ne表示所有的next指针是多少,相当与n个单链表 int h[N],e[M],ne[M],idx,n; int ans = N;//全局的答案存储最小的最大值 //深度优先搜索和宽度优先搜索每个点只会遍历一次 bool st[N];//存一下那些点已经被遍历过了,就不要再遍历他了 void add(int a,int b){//a前插入b e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } //树和图的深度优先搜索的代码 时间复杂度是O(n + m),因为点数和边数呈线性关系 //u表示已经遍历到这个节点了 //以u为根的子树中点的数量 int dfs(int u){ st[u] = true;//首先先标记下当前这个点已经被搜索过了 //记录当前子树的大小 int sum = 1; int res = 0;//表示当前连通块的最小值的最大值 //遍历下u的所有的初边 for(int i = h[u];i != -1;i = ne[i]){ // 存储当前结点对应图里边结点的编号是多少 int j = e[i]; //如果当前点没有做过的话,就一直搜,一条路走到黑 if(!st[j]) { //表示当前子树的大小 int s = dfs(j); //当前的子树也算是一个连通块 res = max(res,s); //当前子树也是我们的子树的一部分 //s为根的子树是以u为根节点的子树的一部分 sum += s; } } res = max(res,n - sum); // cout << u << " " << sum; //最后res存的就是删除当前这个点之后所存储的最大的点数了 ans = min(res,ans); return sum; } int main(){ //头结点指向-1,n给单链表的头结点指向-1 memset(h,-1,sizeof h); cin >> n; for(int i = 0;i < n - 1;i++){ int a,b; cin >> a >> b; add(a,b),add(b,a); } dfs(1);//从第一个点开始搜索 cout << ans; }