原题网址:https://leetcode.com/problems/minimum-height-trees/
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.
Format
The graph contains n
nodes which are labeled from 0
to n - 1
. You will be given the number n
and a list of undirected edges
(each edge is a pair of labels).
You can assume that no duplicate edges will appear in edges
. Since all edges are undirected, [0, 1]
is the same as [1, 0]
and thus will not appear together in edges
.
Example 1:
Given n = 4
, edges = [[1, 0], [1, 2], [1, 3]]
0 | 1 / \ 2 3
return [1]
Example 2:
Given n = 6
, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0 1 2 \ | / 3 | 4 | 5
return [3, 4]
Hint:
- How many MHTs can a graph have at most?
Note:
(1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.”
(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
方法一:最小高度树的根一定在图的一条最长路径的中间,寻找这条最长路径的方法是从任意一点出发,找到最远的点,然后再从这个最远的点出发,找到离它最远的点。可以用广度优先或者深度优先搜索。public class Solution {
private int maxNode, maxDepth;
private void dfs(int from, int depth, List<Integer>[] graph, boolean[] visited, int[] dist, int[] prev) {
if (depth > maxDepth) {
maxDepth = depth;
maxNode = from;
}
for(int next: graph[from]) {
if (visited[next]) continue;
visited[next] = true;
prev[next] = from;
dist[next] = depth+1;
dfs(next, depth+1, graph, visited, dist, prev);
}
}
private List<Integer> roots = new ArrayList<>();
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer>[] graph = new ArrayList[n];
for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
for(int i=0; i<edges.length; i++) {
graph[edges[i][0]].add(edges[i][1]);
graph[edges[i][1]].add(edges[i][0]);
}
boolean[] visited = new boolean[n];
int[] dist1 = new int[n];
int[] prev1 = new int[n];
maxNode = 0;
maxDepth = 0;
visited[0] = true;
dfs(0, 0, graph, visited, dist1, prev1);
int node1 = maxNode;
int[] dist2 = new int[n];
int[] prev2 = new int[n];
Arrays.fill(visited, false);
maxNode = node1;
maxDepth = 0;
visited[node1] = true;
dfs(node1, 0, graph, visited, dist2, prev2);
int node2 = maxNode;
int node = node2;
for(int i=0; i<maxDepth/2; i++) node = prev2[node];
if ((maxDepth & 1) == 0) {
roots.add(node);
} else {
roots.add(node);
roots.add(prev2[node]);
}
return roots;
}
}
优化,利用prev特性避免使用visit数组:
public class Solution {
private void find(int curr, int dist, int[] prev, List<Integer>[] graph, Node max) {
if (dist >= max.dist) {
max.dist = dist;
max.id = curr;
}
for(int i=0; i<graph[curr].size(); i++) {
int next = graph[curr].get(i);
if (next == prev[curr]) continue;
prev[next] = curr;
find(next, dist+1, prev, graph, max);
}
}
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer>[] graph = new List[n];
for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
for(int[] edge: edges) {
graph[edge[0]].add(edge[1]);
graph[edge[1]].add(edge[0]);
}
int[] prev = new int[n];
Arrays.fill(prev, -1);
Node node1 = new Node();
find(0, 0, prev, graph, node1);
Arrays.fill(prev, -1);
Node node2 = new Node();
find(node1.id, 0, prev, graph, node2);
List<Integer> roots = new ArrayList<>();
int id = node2.id;
for(int i=0; i<node2.dist/2; i++) id = prev[id];
roots.add(id);
if (node2.dist % 2 == 1) roots.add(prev[id]);
return roots;
}
}
class Node {
int id = -1;
int dist = Integer.MIN_VALUE;
}
方法二:动态规划。
public class Solution {
private int[] length;
private int[][] height;
private List<Integer>[] graph;
private void dfs(int parent, int current) {
for(int next: graph[current]) {
if (next == parent) continue;
dfs(current, next);
int h = height[next][0] + 1;
if (h > height[current][0]) {
height[current][1] = height[current][0];
height[current][0] = h;
} else if (h > height[current][1]) {
height[current][1] = h;
}
}
}
private int min;
private void dfs(int parent, int current, int longest) {
length[current] = Math.max(longest, height[current][0]);
if (length[current] < min) min = length[current];
for(int next: graph[current]) {
if (next == parent) continue;
int nl = Math.max(longest+1, height[next][0]+1==height[current][0]? height[current][1]+1 : height[current][0]+1);
dfs(current, next, nl);
}
}
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Integer> roots = new ArrayList<>();
if (n==0) return roots;
if (n<=2) {
for(int i=0; i<n; i++) roots.add(i);
return roots;
}
min = n;
length = new int[n];
height = new int[n][2];
graph = new List[n];
for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
for(int i=0; i<edges.length; i++) {
graph[edges[i][0]].add(edges[i][1]);
graph[edges[i][1]].add(edges[i][0]);
}
dfs(-1, 0);
dfs(-1, 0, 0);
for(int i=0; i<n; i++) {
if (length[i] == min) roots.add(i);
}
return roots;
}
}
优化,将参数定义为高度和半径,含义更清晰:
public class Solution {
private int min = Integer.MAX_VALUE;
private int[] radius;
private int[][] height;
private List<Integer>[] graph;
private void calcHeight(int prev, int curr) {
for(int i=0; i<graph[curr].size(); i++) {
int next = graph[curr].get(i);
if (next == prev) continue;
calcHeight(curr, next);
int h = height[next][0] + 1;
if (h > height[curr][0]) {
height[curr][1] = height[curr][0];
height[curr][0] = h;
} else if (h > height[curr][1]) {
height[curr][1] = h;
}
}
}
private void calcRadius(int prev, int curr, int sum) {
radius[curr] = Math.max(sum, height[curr][0]);
if (radius[curr] < min) min = radius[curr];
for(int i=0; i<graph[curr].size(); i++) {
int next = graph[curr].get(i);
if (next == prev) continue;
int nextSum;
if (height[next][0]+1 == height[curr][0]) nextSum = Math.max(sum, height[curr][1]) + 1;
else nextSum = Math.max(sum, height[curr][0]) + 1;
calcRadius(curr, next, nextSum);
}
}
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
radius = new int[n];
height = new int[n][2];
graph = new List[n];
for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
for(int[] edge: edges) {
graph[edge[0]].add(edge[1]);
graph[edge[1]].add(edge[0]);
}
calcHeight(-1, 0);
calcRadius(-1, 0, 0);
List<Integer> roots = new ArrayList<>();
for(int i=0; i<n; i++) {
if (radius[i] == min) roots.add(i);
}
return roots;
}
}
啰嗦一点的方法:
public class Solution {
private int[] radius;
private int[][] height;
private int[][] htnode;
private List<Integer>[] graph;
private void find(int prev, int node) {
for(int next: graph[node]) {
if (next == prev) continue;
find(node, next);
if (height[next][0] + 1 > height[node][0]) {
height[node][0] = height[next][0] + 1;
htnode[node][0] = next;
}
}
for(int next: graph[node]) {
if (next == prev) continue;
if (next == htnode[node][0]) continue;
if (height[next][0] + 1 > height[node][1]) {
height[node][1] = height[next][0] + 1;
htnode[node][1] = next;
}
}
}
private void find(int prev, int node, int sum) {
radius[node] = Math.max(sum, height[node][0]);
for(int next: graph[node]) {
if (next == prev) continue;
if (next == htnode[node][0]) {
find(node, next, Math.max(sum+1, height[node][1]+1));
} else {
find(node, next, Math.max(sum+1, height[node][0]+1));
}
}
}
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
radius = new int[n];
height = new int[n][2];
htnode = new int[n][2];
graph = new List[n];
for(int i=0; i<n; i++) graph[i] = new ArrayList<>();
for(int[] edge: edges) {
graph[edge[0]].add(edge[1]);
graph[edge[1]].add(edge[0]);
}
find(-1, 0);
find(-1, 0, 0);
int min = Integer.MAX_VALUE;
for(int r: radius) min = Math.min(min, r);
List<Integer> roots = new ArrayList<>();
for(int i=0; i<radius.length; i++) {
if (radius[i] == min) roots.add(i);
}
return roots;
}
}