无根树的直径与重心

无根树:由无向(有向)连通图构成,如果连通图中由环,根据dfs生成的序列构成dfs深度优先生成树,广度也可.都可以称为无根树.

树的直径:树上存在俩点之间的路径权重之和最大,称为树的直径
树的重心:树上存在一点,以其为根,使得树上所有节点为根的子树节点数最小,重心最多存在两个.

红色部分树的直径:
无根树的直径与重心_第1张图片

求树的直径的方法:
1.两次bfs/dfs
思想:随机一个节点,进行一次dfs遍历,找到该节点最远的一个节点,再以该节点所对应的最远节点再进行一次dfs遍历即可.

一次dfs找到以1为根的最长路径节点为6
无根树的直径与重心_第2张图片
第二次以6为根节点进行dfs找到7为最远距离,故此以6为根,到7的dis[7] 的距离就是树的直径
无根树的直径与重心_第3张图片

优点:可记录树上多个属性(如路径、子树孩子节点)
缺点:不能处理负权边!!!

void dfs(int x, int parent, int value)
{
	dis[x] = value;
	for(int i = head[x]; i; i = Next[i]){
		int y = ver[i];
		if(y == parent) continue;
		dfs(y, x, value + w[i]);  //w[i]为边权值
	}
}

//寻找以x为根的路径最大值,即为x的最远节点 u:
dfs(x, -1, 0)
int u = x;
for(int i = 1; i <= n; i++) if(dis[i] > dis[u]) u = i;
//在以u为节点进行遍历搜索最远节点。
dfs(u, -1, 0)
for(int i = 1; i <= n; i++) if(dis[i] > dis[u]) u = i;
//dis[u]即为最远距离

2 树形dp求树的直径:
思想:用dp[x]表示 叶子节点到节点x之间的最长距离,先dfs到叶子节点处,在回溯的过程中更新dp即可.
优点:可以处理负权边的树
缺点:不易存储树的相关信息

int dpTree(int x,int parent)
{
	int ans = -0x3f3f3f3f;
	for(int i = head[x]; i ; i = Next[i]){
		int y = ver[i];
		if(y == parent) continue;
		ans = dpTree(y, x);//遍历至叶子节点处
		ans = max(ans, dp[x] + dp[y] + w[i]);//记录以x为根的子树直径
		dp[x] = max(dp[x], dp[y] + w[i]);//更新到达x的最长路径
	}
	return ans;
}

树的重心:
思想:找到一个点,使其为根的每个节点所有的子节点数最小。在子树中找到最大子树节点数量,在最大子树节点数量中找最小值。也要看向上的节点数量-去当前的子树数量。
siz[x]表示以x为根的子树节点数量, dp[x] 表示以x为根子树中最大节点数量
step1:先随机一个点为根,dfs至叶子节点处,在递归至叶子节点处时,初始化每个节点为根时siz[x]的值为1(根也算一个节点)
step2:最在回溯过程种,更新siz[x],dp[x]
step3:回溯结束后,在dp中找到最小值

无根树的直径与重心_第4张图片

void dfs2(int x, int parent)
{
	siz[x] = 1;
	for(int i = head[x]; i ; i = Next[i]){
		int y = ver[i];
		if(y == parent) continue;
		siz[x] += siz[y];
		dp[x] = max(dp[x],siz[y]);
	}
	dp[x] = max(dp[x], n - siz[x]);//n为总节点数量, 求向上的节点数量
	if(minNums > dp[x]){
		minNums = dp[x];
		ans = x;
	}
}

你可能感兴趣的:(无根树的直径与重心)