对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。
格式
该图包含 n
个节点,标记为 0
到 n - 1
。给定数字 n
和一个无向边 edges
列表(每一个边都是一对标签)。
你可以假设没有重复的边会出现在 edges
中。由于所有的边都是无向边, [0, 1]
和 [1, 0]
是相同的,因此不会同时出现在 edges
里。
示例 1:
输入:n = 4, edges = [[1, 0], [1, 2], [1, 3]]
0 | 1 / \ 2 3 输出:[1]
示例 2:
输入:n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0 1 2 \ | / 3 | 4 | 5 输出:[3, 4]
说明:
- 根据树的定义,树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
- 树的高度是指根节点和叶子节点之间最长向下路径上边的数量。
思路:这道题可以一个一个节点用BFS判断对于每个节点的最小高度树,但是这样的时间复杂度是O(n^n),复杂度很高,对于大数据集无法通过测试,所以要采用其他思路。这里可以采用逐个删除点的方法来做,首先我们构造出无向图,把度为1的节点(出度+入度)删除,当然对应的边也删除,我们一直重复操作直到整个图中只剩下1或者两个节点,最后返回剩余的1个或者两个节点即可,这么做的依据是因为最小高度树的节点一定在图的拉成最长线性节点的中点位置,我们需要找到最长的线性节点,取中间节点即可。具体代码的思路是:需要一个辅助队列来存储度为1的节点,持续进行如上所说的BFS操作,直至剩余的节点是2个或者1个,返回队列q的队头元素或者前两个对头元素。
参考代码:
class Solution {
public:
vector<unordered_set<int>> make_graph2(int n, vector<pair<int, int>>& edges) {
vector<unordered_set<int>> graph(n);
for (auto it : edges) {
graph[it.first].insert(it.second);
graph[it.second].insert(it.first);
}
return graph;
}
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
if (n <= 0) return {};
if (n == 1) return { 0 };
if (n == 2) return { 0,1 };
vector<unordered_set<int>> graph = make_graph2(n, edges);
queue<int> q;
for (int i = 0; i < graph.size(); i++) {
if (graph[i].size() == 1) {
q.push(i);
}
}
int left = n;
while (!q.empty()) {
int reductNumber = 0;
int tmp = q.size();
for (int i = q.size(); i > 0; i--) {
int top = q.front(); q.pop();
for (auto neibor : graph[top]) {
graph[neibor].erase(top);
if (graph[neibor].size() == 1) {
reductNumber++;
q.push(neibor);
}
}
}
left -= tmp;
if (left <= 2) break;
}
if (left == 2) {
int top = q.front(); q.pop();
int sed = q.front(); q.pop();
return { top,sed };
}
return { q.front() };
}
};