题目:
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]
题意及分析:给出一个无向图,求能形成树的最矮的树的根节点。我首先想到的是用bfs,先保存每个点的邻节点。然后使用bfs遍历当前还未被遍历过的邻节点,但是代码运行超时。。。看了网上使用较多的一种方法是,每次删除度为1的点,直至剩下的点小于等于2,那么这1,2个点就是求的结果。
超时代码:
public class Solution { public List<Integer> findMinHeightTrees(int n, int[][] edges) { List<Integer> res = new ArrayList<>(); int minHeight = Integer.MAX_VALUE; //当前最小的高度 HashMap<Integer,List<Integer>> neighbors = new HashMap<>(); //key为节点,list为该节点的邻接节点 for(int i=0;i<edges.length;i++){ //找出每个点的邻接节点,这样方便查找 if(neighbors.containsKey(edges[i][0])){ neighbors.get(edges[i][0]).add(edges[i][1]); }else{ List<Integer> list = new ArrayList<>(); list.add(edges[i][1]); neighbors.put(edges[i][0],list); } if(neighbors.containsKey(edges[i][1])){ neighbors.get(edges[i][1]).add(edges[i][0]); }else{ List<Integer> list = new ArrayList<>(); list.add(edges[i][0]); neighbors.put(edges[i][1],list); } } for(int i=0;i<n;i++){ //对每个节点求其高度 int max = 0; //以点i为跟节点时,树的最高高度 boolean[] visited = new boolean[n]; //记录该点是否被遍历过 Queue<Integer> queue = new LinkedList<>(); Queue<Integer> tempQueue = new LinkedList<>(); //用来记录下一层节点 queue.add(i); max++; while(!queue.isEmpty()||!tempQueue.isEmpty()){ if(!queue.isEmpty()){ //遍历一层 int a = queue.poll(); visited[a] = true; if(neighbors.get(a)!=null){ for(int j=0;j<neighbors.get(a).size();j++){ //未被遍历过的邻节点添加进queue int neighbor = neighbors.get(a).get(j); if(!visited[neighbor]) tempQueue.add(neighbor); } } }else{ //讲下一层数据添加当前层 if(!tempQueue.isEmpty()) //下一层不为空,层数加一 max++; queue = new LinkedList<>(tempQueue); tempQueue.clear();; } } if(max<minHeight){ minHeight=max; res.clear(); res.add(i); }else if(max==minHeight){ res.add(i); } } return res; } }
第二种方法(直接使用一个map[]数组保存是否入度,但是这样删除入度为1的点时需要遍历数组查找,时间复杂度还是较大):
public class Solution { public List<Integer> findMinHeightTrees(int n, int[][] edges) { List<Integer> res = new ArrayList<>(); for(int i=0;i<n;i++){ res.add(i); } if(res.size()<=2) return res; int[] map = new int[n]; for(int i=0;i<edges.length;i++){ //找出每个点的邻接节点,这样方便查找 map[edges[i][0]]++; map[edges[i][1]]++; } Queue<Integer> queue = new LinkedList<>(); Queue<Integer> tempQueue = new LinkedList<>(); //用来记录下一层需要删除的点 for(int i=0;i<map.length;i++){ if(map[i]==1) queue.add(i); //最初始度为1的点 } while((!queue.isEmpty()||!tempQueue.isEmpty())){ if (!queue.isEmpty()){ int temp = queue.poll(); res.remove((Object)temp); //删除度为1的点,且重置度 for(int i = 0;i<edges.length;i++){ //找到以该节点为边的点,将值该点和邻节点度减1 if(edges[i][0]==temp){ map[temp]--; //为0了。 map[edges[i][1]]--; if(map[edges[i][1]]==1){ tempQueue.add(edges[i][1]); //删除该点后若和该点相连的点度也变成1则添加进queue } } if(edges[i][1]==temp){ map[temp]--; map[edges[i][0]]--; if(map[edges[i][0]]==1){ tempQueue.add(edges[i][0]); //删除该点后若和该点相连的点度也变成1则添加进queue } } } }else{ //遍历下一层 queue=new LinkedList<>(tempQueue); tempQueue.clear();; if(res.size()<=2) break; } } return res; } }
第二种方法:使用一个hashmap保存每个点的所有邻接节点,这样就不需要遍历数组,减少时间复杂度。
public class Solution { public List<Integer> findMinHeightTrees(int n, int[][] edges) { List<Integer> res = new ArrayList<>(); for(int i=0;i<n;i++){ res.add(i); } if(res.size()<=2) return res; HashMap<Integer,List<Integer>> neighbors = new HashMap<>(); //key为节点,list为该节点的邻接节点 for(int i=0;i<edges.length;i++){ //找出每个点的邻接节点,这样方便查找 if(neighbors.containsKey(edges[i][0])){ neighbors.get(edges[i][0]).add(edges[i][1]); }else{ List<Integer> list = new ArrayList<>(); list.add(edges[i][1]); neighbors.put(edges[i][0],list); } if(neighbors.containsKey(edges[i][1])){ neighbors.get(edges[i][1]).add(edges[i][0]); }else{ List<Integer> list = new ArrayList<>(); list.add(edges[i][0]); neighbors.put(edges[i][1],list); } } Queue<Integer> queue = new LinkedList<>(); for(int i=0;i<n;i++){ if(neighbors.get(i)!=null){ if(neighbors.get(i).size()==1){ queue.add(i); } } } Queue<Integer> tempQueue = new LinkedList<>(); while (!queue.isEmpty()||!tempQueue.isEmpty()){ if(!queue.isEmpty()){ int temp = queue.poll(); //查找该点的邻节点 res.remove((Object)temp); List<Integer> neighbor = neighbors.get(temp); //度为1的点的邻节点肯定只有一个 for(int i=0;i<neighbor.size();i++){ //在邻节点中删除节点 neighbors.get(neighbor.get(i)).remove((Object)temp); if(neighbors.get(neighbor.get(i)).size()==1){ //入度为1 tempQueue.add(neighbor.get(i)); } } }else{ queue = new LinkedList<>(tempQueue); tempQueue.clear(); if(res.size()<=2) break; } } return res; } }