数和图的存储方式与遍历
数和图的存储方式:
一般有两种
树是一种特殊的图(即无环联通图)。所以下面只讲图。
图的话分为两种:①有向图(边是有方向的:a➡️b)和
②无向图(边是无方向的:a——b)。这里无向图可以定义:a➡️b和b⬅️a来表达无向图
无向图可以看成特殊的有向图,所以接下来分析有向图
有向图,存储有两大类{
①:邻接矩阵,开个二维数组g[a][b]表示a➡️b,如果有权重,则g[a][b]表示权值。
邻接矩阵不能保存重复的边,若有的重边话只能保留一条,适合于最短路,空间复杂度为n^2。
②:邻接表——单链表。每个点上都有一个单链表,若有n个点就开n个单链表,每一个节点上开一个表。
每一个点存一个路径,就是存这个点能走到哪一个点。这里的路径无关顺序,也就是说除了根节点其余的都是子节点。插入方法和链表的插入方法一致
}
数和图的遍历:
有两种:①深度优先遍历,②宽度优先遍历
①:选取一个起点,从这个起点开始,向下DFS
②:BFS
题目:846. 树的重心 - AcWing题库
思路:
本题是无向边,所以要建两条边互相指向。
"重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。"
题目这句话的意思是:将某一个点删除之后,然后将剩下的每一块区域中含有多少
节点给统计出来,取一个最大值。循环操作,记录每一个节点删除后,所剩下区域的最大值都给记录出来然后在所有最大值里面取一个最小值,这个最小值就是题目所要求解。
图示:
代码:
~~暴力无从下手~~
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n;
const int N=1e5+10,M=N*2;
//h[u]即每一个点都可以作为头节点,向下遍历
int h[N],e[M],ne[M],idx;
//每个点只搜索一次
bool st[N];
//所有最大值中的最小值
int ans=N;
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx,idx++;
}
//从u开始向下遍历。返回以u为根节点的子树中点的数量
int dfs(int u)
{
//标记一下,当前这个点已经被搜过了
st[u]=true;
//sum为以此节点作为根节点区域的所有节点的数量,res表示删去这个节点后 所有连通块中的最大值
int sum=1,res=0;
//遍历一下u的所有可能移动的边,i每一次向下移动一个即等于当前点的下一个指向
//是从头开始遍历的,所以不需要恢复操作
for(int i=h[u]; i!=-1 ;i=ne[i])
{
//当前这个点对应图中节点的编号是多少
int j=e[i];
//这个点只使用一次,不需要回溯也就不需要恢复st[j]的bool值
if(!st[j])
{
int s=dfs(j);
//取这个区域和上个区域连通块节点数量的最大值
res=max(s,res);
//计算以此节点作为根节点区域的所有节点的数量
sum+=s;
}
}
//不是以这个点作为根节点之外的区域的所有节点数量(n-sum),与根节点区域里的节点数量(res)取最大值
res=max(res,n-sum);
//和所有的最大值取一个最小值
ans=min(res,ans);
//返回一块区域的节点数量
return sum;
}
int main()
{
//每个点都可以作为根节点,所以初始化为-1
memset(h,-1,sizeof h);
cin >> n;
for(int i=0;i<n;i++)
{
int a,b;
cin >> a >> b;
//因为是无向边,所以可以认为是互相指向
add(a,b),add(b,a);
}
//从哪一个节点深搜都是可以的,因为ans总能更新成最大值里的最小值
dfs(1);
cout << ans;
return 0;
}
tips:
这里的dfs中不需要恢复st[ j ]的原因是这里只需要遍历一遍所有的节点,然后求出以u作为根节点的数量即可。