1树的存储
vector<int>g[N];//这里用邻接表
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
2树的遍历
简单的dfs
void dfs(int u,int fa)//u为当前节点,fa是从哪个节点来的
{
for(int v:g[u])
{
if(v==fa)continue;//防止原路返回
dfs(v,u);
}
}
dfs(1,0);
3树的直径
概念:树上任意两点之间的最长的简单路径。
性质:
1树上任何一个点能走到的最远的点一定是直径的端点。
2
求树的直径
1两次dfs,从任意点出发走到最远点,再从该最远点走到最远点。
int dis[N];//某点到其他点的距离
int w[N];路径的权值,这里默认为1;
void(int u,int fa)
{
for(int v:g[u])
{
if(fa==v)//不原路返回
continue;
dis[v]=dis[u]+1;
dfs(v,u);
}
}
int ans1=1;//记录距离最大的点
for(int i=1;i<=n;i++)//遍历寻找距离最远的点
{
if(dis[i]>dis[ans])
ans1=i;
}
//ans1为所求的第一个端点,还需要从ans1开始找到最远点。
2树形dp,求每个点向下的最远和次远的和,取max。
vector<int>f(n+1),k(n+1)//f为最远距离,k为次远距离。
int ans=0;
void(int u,int fa)
{
for(int v:g[u])
{
if(fa==v)continue;
dfs(v,u);
if(f[v]+1>f[u])//子节点最远距离加1大于当前点,就更新
{
k[u]=f[u];
f[u]=f[v]+1;
}
else if(fa[v]+1>k[u])//更新次节点
{
k[u]=fa[v]+1;
}
}
ans=max(ans,fa[u]+k[u]);
}
4LCA最近公共祖先
(两个节点的最近公共祖先)
倍增法
记录父亲节点
int father[N];
int fa[N][25];//记录第i个点上方2^j个父亲节点。
for(int i=1;i<=n;i++)
{
fa[i][0]=father[i];
}
for(int j=1;(1<<j)<=n;j++)
{
for(int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
}
int h[N];记录每个节点的深度,在树的遍历可以得到。
int lca(int a,int b)//查找a与b的最近公共祖先。
{
if(h[b]>h[a])//让a成为更深的,对a作往上爬的动作。
{
int t=a;
a=b;
b=a;
}
}
for(int i=20;i>=0;i--)
{
if(h[a]-(1<<i)>=b)
a=fa[a][i];//a向上爬
}
//遍历结束后,a与b的深度相同
if(a==b)//如果a与b相遇,说明b是原来a的祖先之一
{
return b;
}
for(int i=20;i>=0;i--)
{
if(fa[a][i]!=fa[b][i])//如果不是同一个点就往上爬,如果是的话就往下看。
{
a=fa[a][i];
b=fa[b][i]
}
}
return fa[a][0];//最终爬到了lca下面
5树的重心
概念:某节点最大子树的节点数最少,该节点就是重心。
求树的重心
int centroid;//记录重心;
int dfs(int u,int fa)
{
int siz=1,mx=0;//mx记录该节点子树的最大值
for(int v:g[u])
{
if(v==fa)
continue;
int son=dfs(v,u);//记录子节点方向子树的节点数
int siz+=son;
mx=max(mx,son);
}
mx=max(mx,n-siz);//和父节点方向的子树节点数比较。
if(mx*2<=n)
centroid=u;//最大的子树不超过总节点的一半,u为重心。
return siz;
}
6最小生成树
在一个有边权的图中,得到一个边权和最小的树。
(1)KRUSKAL
struct node{
int from,to,w;//起点,终点,权值。
friend bool operator <(const node&a,const node&b)
{
return a.w<b.w;
}//排序
}edge[N]//存储边
int fa[N];//父节点
void init()
{
for(int i=0;i<n;++i)
fa[i]=i;
}
int find(int x)//寻找父节点
{
int x2=x,pre;
while(x2!=fa[x2])x2=fa[x2];
while(x!=fa[x1])
{
pre=x;
x=fa[x];
fa[pre]=x2;
}
return x2;
}
void merge(int x,int y)//合并
{
x=find(x);
y=find(y);
if(x!=y)
fa[x]=y;
return;
}
vector<node>tree;//记录构成树的边
int kruskal(int n,int m);//n为点数,m为边数
{
sort(edge,edge+m);
int sum=0;//边权和
int cnt=0;//树的边的边数
for(int i=0;i<m&&cnt!=n-1;i++)//n-1是最终树的边数
{
int from=edge[i].from;//起点
int to=edge[i].to;//终点
if(find(from)!=find(to))//如果不在同一颗树上
{
cnt++;
merge(from,to);
sum+=edge[i].w;
tree.push_back(edge[i]);//把该边加入树内
}
}
return sum;
}