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.
Solution
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector<vector<int>> graph(n, vector<int>{});
for (int i = 0; i < edges.size(); i++) {
graph[edges[i].first].push_back(edges[i].second);
graph[edges[i].second].push_back(edges[i].first);
}
// find a end point
int firstEnd = 0;
for (int i = 0; i < graph.size(); i++) {
if (graph[i].size()) {
firstEnd = getEndp(i, graph, n);
break;
}
}
// get another end point from the first point
// then we get a longest path between this two points
int longestLength = getHeight(firstEnd, graph, n);
// find the one or two points in the middle
return getMid(firstEnd, graph, n, longestLength);
}
getMid
函数
inline vector<int> getMid(int currentRoot, vector<vector<int>>& graph, int n, int height) {
vector<int> rootLabels;
vector<bool> visited(n, false);
vector<int> pre(n);
queue<int> nodes; nodes.push(currentRoot);
visited[currentRoot] = true;
int lastNode = currentRoot;
while (!nodes.empty()) {
int curr = nodes.front();
visited[curr] = true;
for (int dst : graph[curr]) {
if (!visited[dst]) {
pre[dst] = curr;
nodes.push(dst);
}
}
lastNode = curr;
nodes.pop();
}
bool even = height % 2 == 0;
for (int i = 0; i < height + 1; i++) {
cout << lastNode << " ---> ";
if (even && i == height/2 - 1) rootLabels.push_back(lastNode);
else if (i == height/2) {
rootLabels.push_back(lastNode);
}
lastNode = pre[lastNode];
}
cout << "null" << endl;
return rootLabels;
}
分析
首先通过分析可知,这样的点肯定是在整个图的最中间。
我们可以通过这样的方法来找到合适的中间点:
- 通过
getEndp
函数,使用一个BFS找到一个叶节点(记为firstEnd
)。 - 再从这个叶节点出发,使用
getHeight
函数,同样是一个BFS找到从这个叶节点开始的最长的路径,记录下来。 - 再进行一次BFS,
getMid
找到位于这条最长路径的中间点(1个或2个)。 - 返回中间点。
getMid
使用pre
数组记录路径;getMid
和getHeight
这两步当然是可以合并的,一次BFS就可以算出两个,但是讲解就不清晰了
可以分析,BFS的时间复杂度是O(n),该解法使用了3次(应该是2次)BFS,所以整体的复杂度还是O(n)。
空间复杂度上,当然是和BFS相同,也为O(n)。