Description:
For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels.
Solution:
Change this problem to finding the diameter of a tree, and use two dfs can solve it.
Use three arrays, first[], firstFrom[], and second[].
first[i] is the longest path started from node i.
second[i] is the second path started from node i.
firstFrom[i] is the where the longest path from i lead to.
Two dfs:
1. DownToUp: this if the first dfs. The task here is start from any node (I use 0 here), and calculate each node first, firstFrom, and second considering only the nodes that are child node.
2. UpToDown: since we have known the first, and second for each node of its child node, then we need to consider its parent node. (and that is why we need firstFrom and dfs from Up to Down)
import java.util.*;
public class Solution {
int n;
int head[];
edge edges[];
int tot = 0;
int[] first;
int[] firstFrom;
int[] second;
boolean[] visit;
public void addEdges(int u, int v) {
edges[tot] = new edge(v);
edges[tot].next = head[u];
head[u] = tot++;
}
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> list = new LinkedList<Integer>();
this.n = n;
this.head = new int[n];
Arrays.fill(this.head, -1);
this.edges = new edge[n * 3];
this.first = new int[n];
this.second = new int[n];
this.firstFrom = new int[n];
this.visit = new boolean[n];
for (int i = 0; i < n - 1; i++) {
int u = edges[i][0];
int v = edges[i][1];
addEdges(u, v);
addEdges(v, u);
}
Arrays.fill(visit, false);
visit[0] = true;
dfsDownToTop(0);
Arrays.fill(visit, false);
dfsTopToDown(0);
int min = n;
for (int i = 0; i < n; i++) {
if (first[i] < min) {
min = first[i];
list.clear();
list.add(i);
} else if (first[i] == min) {
list.add(i);
}
}
return list;
}
public void dfsDownToTop(int u) {
for (int e = head[u]; e != -1; e = edges[e].next) {
int v = edges[e].v;
if (visit[v])
continue;
visit[v] = true;
dfsDownToTop(v);
if (first[v] + 1 > first[u]) {
second[u] = first[u];
first[u] = first[v] + 1;
firstFrom[u] = v;
} else if (first[v] + 1 >= second[u]) {
if (first[v] + 1 == first[u])
firstFrom[u] = -1;
second[u] = first[v] + 1;
}
}
return;
}
public void dfsTopToDown(int u) {
int len;
for (int e = head[u]; e != -1; e = edges[e].next) {
int v = edges[e].v;
if (visit[v])
continue;
if (firstFrom[u] == v)
len = second[u] + 1;
else
len = first[u] + 1;
if (len > first[v]) {
second[v] = first[v];
first[v] = len;
firstFrom[v] = u;
} else if (len == first[v]) {
firstFrom[v] = -10;
second[v] = len;
} else if (len > second[v])
second[v] = len;
}
for (int e = head[u]; e != -1; e = edges[e].next) {
int v = edges[e].v;
if (visit[v])
continue;
visit[v] = true;
dfsTopToDown(v);
}
return;
}
class edge {
int v;
int next;
edge(int v) {
this.v = v;
next = -1;
}
}
}