For an 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 :
Input:n = 4
,edges = [[1, 0], [1, 2], [1, 3]]
0 | 1 / \ 2 3 Output:[1]
Example 2 :
Input:n = 6
,edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0 1 2 \ | / 3 | 4 | 5 Output:[3, 4]
Note:
- 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.”
- The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf.
思路:第一感是dfs,思路类似https://leetcode.com/problems/sum-of-distances-in-tree/
先从0开始dfs,得到后面的结点n到叶节点的长度(只需要维护前2长的长度,方向0—>i),存到route[i][2]数组中。
route[i][2]用于后面求某节点i到所有叶结点的最长深度。包括方向i—>0。
为什么要维护每个节点到叶结点前2长的长度?因为后面求节点i到所有叶结点的最长深度(这一步可以bfs或dfs),要基于i的前一个节点j的前2的长度,因为j最长那条可能就在j—>i的方向,以此类推(求此长度是沿着方向0—>i)。这一步还要注意一些细节。
最后返回最长长度的两个节点。时间复杂度O(n),空间复杂度O(n).
方法一.dfs(11ms)
class Solution {
int len;
ArrayList<Integer> res;
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
res=new ArrayList<>();
ArrayList<Integer>[] graph=(ArrayList<Integer>[])new ArrayList[n];
for(int i=0;i<graph.length;i++) graph[i]=new ArrayList<Integer>();
for(int[] e:edges){
graph[e[0]].add(e[1]);
graph[e[1]].add(e[0]);
}
int[][] route=new int[n][2];//存node节点前2大的距离
boolean[] mark=new boolean[n];
dfs(graph,0,mark,route);
LinkedList<Integer> queue=new LinkedList<>();
queue.add(0);
len=route[0][0];
res.add(0);
mark[0]=false;
while(queue.size()>0){
int node=queue.poll();
for(Integer m:graph[node]){
if(mark[m]){
mark[m]=false;
if(route[node][0]-1>route[m][0]){//父节点在另外一条路径,这条路径之后的排除
continue;
}
// =的情况
queue.add(m);
if(route[node][1]>=route[m][0]){//route[node][1]另外一条路
route[m][1]=route[m][0];
route[m][0]=route[node][1]+1;
}else if(route[node][1]>=route[m][1]){
route[m][1]=route[node][1]+1;
}
if(route[m][0]==len) res.add(m);
else if(route[m][0]<len){
res.clear();
len=route[m][0];
res.add(m);
}
}
}
}
return res;
}
int dfs(ArrayList<Integer>[] graph,int node,boolean[] mark,int[][] route){
mark[node]=true;
for(int n:graph[node]){
if(!mark[n]){
int a=dfs(graph,n,mark,route);
if(a+1>=route[node][0]){
route[node][1]=route[node][0];
route[node][0]=a+1;
}else if(a+1>route[node][1]){
route[node][1]=a+1;
}
}
}
return route[node][0];
}
}
看了评论区,有一种很巧妙的思路。从所有叶结点开始bfs,每次消去一层叶结点。最后剩余的就是要返回的节点了。感觉这种思路挺难想到的。
方法二:bfs(40ms) 。大概是要用hashset存每条边,计算hash值比较耗时吧,应该还能优化。
class Solution {
public List<Integer> findMinHeightTrees(int n, int[][] edges) {
List<Set<Integer>> graph=new ArrayList<>();
List<Integer> leaves=new ArrayList<>();
for(int i=0;i<n;i++) graph.add(new HashSet<Integer>());
for(int[] e:edges){
graph.get(e[0]).add(e[1]);
graph.get(e[1]).add(e[0]);
}
for(int i=0;i<n;i++){
if(n==1||graph.get(i).size()==1) leaves.add(i);//包括当只有一个元素时的边界情况
}
int size=n;
while(size>2){//最后剩小于2个
size-=leaves.size();
List<Integer> newleaves=new ArrayList<>();
for(int leaf:leaves){
int node=graph.get(leaf).iterator().next();//set中第一个数,就是下一个节点(其实此时set中只有一个数)
graph.get(node).remove(leaf);//后一个节点移除当前节点
if(graph.get(node).size()==1) newleaves.add(node);
}
leaves=newleaves;
}
return leaves;
}
}