树形dp dfs
题目
解析
树形dp
关于题中描述的图,我们可以整理一下,假设以A为根节点,整理成右侧的树形状。
在以A为根节点的树中,通过dfs可以很容易求得每个节点到叶子结点的最大距离。求解方法如下:
通过递归,问每一个孩子要它到叶子结点的最大距离,然后对每一个孩子的信息进行汇总,就可以得到当前节点到叶子结点的最大距离。举个例子:
我们初始化所有节点到叶子结点的距离为0,递归出口就是叶子结点,它不需要收集它孩子的信息,直接返回即可。
所以G节点返回0,然后F节点在其所有孩子返回的结果中选择最大值,再加1,就是F节点到叶子结点的最大距离1。对于B节点来说,它能收集两个孩子返回来的信息,E:0, F:1。所以,B能知道它到叶子结点的最大距离是2,而且是经过F节点到达的。
也就是说,每个节点能够知道两个信息:
- 到叶子结点的最大距离
- 这个最大距离是经由哪个孩子到达的
此时,我们是求得了以A节点为根情况下的一些信息。这些都是接下来的资源。
在我们以其他节点为根节点求树高前,需要明确一些内容,以B节点为例。此时,不将树重新绘制,只是想象以B为根节点即可。以B为根节点的路径,可以分为向上和向下两部分。向下的部分我用红色表示,向上的部分我用蓝色表示。
对于B节点向下的路径,到叶子结点的最大距离,我们已经求过了,还能知道是经过F节点。
对于B节点向上的路径,到叶子结点的最大距离,就是A节点向下的最大距离,加上AB之间的距离1。
所以,B节点到所有叶子结点的最大距离,就是以上两种情况取max。
但是,还存在特殊情况。A节点在统计其孩子信息时,能够知道到达叶子结点的最大距离是经由A的,那在计算B节点向上的路径时,A节点向下的路径就不能使用经过B点的这条最大路径,而是要使用A节点向下路径中的第二大距离。
也就是说,在【通过dfs求得每个节点到叶子结点的最大距离】时,还需要记录下第二大距离。
于是B向上的最大路径长度就需要分两种情况:
- A向下最大路径经过B:up[B] = d2[A] + 1 第二大距离加1
- A向下最大路径不经过B:up[B] = d1[A] + 1 第一大距离加1
综上,需要分两步求到大叶子结点的距离。
第一步求所有节点向下到达叶子结点的最大距离
第二步求所有节点向上到达叶子结点的最大距离
于是,我们就能够计算每一个节点到所有叶子结点的最大距离了。
在所有节点的最大距离中,再找出最小的。
最后,就是与这个最小值比较,相等就可以加入到结果集中。
答案
var (
// 第一大距离,第二大距离
d1, d2 []int
// 第一大距离经过的孩子
via []int
// 向上
up []int
// 用map表示邻接链表
g map[int][]int
)
func findMinHeightTrees(n int, edges [][]int) []int {
// 初始化
d1, d2, up, via = make([]int, n, n), make([]int, n, n), make([]int, n, n), make([]int, n, n)
// 将图转化成邻接链表表示
g = map[int][]int{}
for _, pair := range edges {
g[pair[0]] = append(g[pair[0]], pair[1])
g[pair[1]] = append(g[pair[1]], pair[0])
}
// 第一步求所有节点向下到达叶子结点的最大距离
dfs1(0, -1)
// 第二步求所有节点向上到达叶子结点的最大距离
dfs2(0, -1)
// 在所有节点的最大距离中,再找出最小的。
minVal := math.MaxInt
for i := 0; i < n; i++ {
minVal = min(minVal, max(up[i], d1[i]))
}
// 就是与这个最小值比较,相等就可以加入到结果集中。
res := []int{}
for i := 0; i < n; i++ {
d := max(up[i], d1[i])
if d == minVal {
res = append(res, i)
}
}
return res
}
// 因为是无向图,用father表示父节点,避免循环遍历
func dfs1(u int, father int) {
for _, next := range g[u] {
if next == father {
continue
}
dfs1(next, u)
d := d1[next] + 1
if d > d1[u] {
d2[u] = d1[u]
d1[u] = d
via[u] = next
} else if d > d2[u] {
d2[u] = d
}
}
}
func dfs2(u int, father int) {
for _, next := range g[u] {
if next == father {
continue
}
// - A向下最大路径经过B:up[B] = d2[A] + 1 第二大距离加1
if via[u] == next {
up[next] = max(d2[u], up[u]) + 1
} else {
// - A向下最大路径不经过B:up[B] = d1[A] + 1 第一大距离加1
up[next] = max(d1[u], up[u]) + 1
}
dfs2(next, u)
}
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
func min(a, b int) int {
if a < b {
return a
}
return b
}