310. 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 :
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.
解题思路
方法一:求最长路径的中点
最小高度树的根节点一定是最长叶到叶节点的路径的中点,找到最长路径的两个叶节点的算法为:
(1)从任意节点开始(这里选0号节点),用bfs找到离该节点最远的叶节点;
(2)从找到的叶节点开始,用bfs找到离该叶节点最远的叶节点;
(3)这两个叶节点间的路径即为最长路径。
为了找到这两个叶节点间的中间节点,可以在bfs时,记录路径长度以及在该路径上每个节点的先前节点,然后从第2个叶节点开始,沿先前节点路径移动一半的路径长度即可,如果路径长度为偶数,则有2个中间节点,否则,只有一个。
代码如下:
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector<vector<int>> graph(n);
vector<int> prev(n);
int len;
for (auto p : edges) {
graph[p.first].push_back(p.second);
graph[p.second].push_back(p.first);
}
int leaf1 = findFarthestLeaf(graph, 0, len, prev);
int leaf2 = findFarthestLeaf(graph, leaf1, len, prev);
for(int i = 0; i < (len - 1) / 2; i++) {
leaf2 = prev[leaf2];
}
if(len % 2) return vector<int>{leaf2};
else return vector<int>{leaf2, prev[leaf2]};
}
int findFarthestLeaf(vector<vector<int>>& graph, int start, int& len, vector<int>& prev) {
int n = graph.size();
vector<bool> visited(n, false);
queue<int> q;
visited[start] = true;
q.push(start);
len = 0;
int leaf;
while(!q.empty()) {
len++;
int size = q.size();
while(size-- > 0) {
int f = q.front();
q.pop();
leaf = f;
for(int nei : graph[f]) {
if (!visited[nei]) {
q.push(nei);
visited[nei] = true;
prev[nei] = f;
}
}
}
}
return leaf;
}
};
方法二:从叶节点开始bfs,逐渐逼近根节点
原理:不断删除叶节点,在删除叶节点后,会生成新的叶节点,循环删除,最后只剩下1个或2个节点,则这1个或2个节点就是最小高度树的根节点。
实现细节:确定一个节点是否是叶节点,可以记录节点的出入度,将叶节点删除后,叶节点的相邻节点的出入度减小1,若变为1,则说明该相邻节点变为新的叶节点。可以用一个vector<bool> visited
记录节点是否被删除。
代码如下:
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
if (n == 1) return {0};
vector<vector<int>> graph(n);
vector<int> res;
queue<int> q;
vector<int> degree(n, 0);
vector<bool> visited(n, false);
for (auto p : edges) {
graph[p.first].push_back(p.second);
graph[p.second].push_back(p.first);
degree[p.first]++;
degree[p.second]++;
}
for (int i = 0; i < n; i++) {
if (degree[i] == 1) {
q.push(i);
visited[i] == true;
}
}
while(n > 2) {
int leafSize = q.size();
n -= leafSize;
while (leafSize-- > 0) {
int leaf = q.front();
q.pop();
for (int leafNeibor : graph[leaf]){
if (!visited[leafNeibor]) {
degree[leafNeibor]--;
if (degree[leafNeibor] == 1) {
q.push(leafNeibor);
visited[leafNeibor] = true;
}
}
}
}
}
while(!q.empty()) {
res.push_back(q.front());
q.pop();
}
return res;
}
};