对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。
格式
该图包含 n 个节点,标记为 0 到 n - 1。给定数字 n 和一个无向边 edges 列表(每一个边都是一对标签)。
你可以假设没有重复的边会出现在 edges 中。由于所有的边都是无向边, [0, 1]和 [1, 0] 是相同的,因此不会同时出现在 edges 里。
示例 1:
示例 2:
说明:
根据树的定义,树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。
树的高度是指根节点和叶子节点之间最长向下路径上边的数量。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-height-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
正向bfs
第一个想到的思路就是枚举起点依次bfs,不断更新答案,然后会超时
class Solution {
public:
int bfs(int begin, int n, vector<vector<int>>& edges)
{
vector<bool> vis(n, false);
deque<int> q; q.push_back(begin);
int ans = 0;
while(!q.empty())
{
ans++;
int qs = q.size();
for(int i=0; i<qs; i++)
{
int tp=q.front(); q.pop_front(); int next=INT_MAX;
vis[tp] = true;
for(int e=0; e<edges.size(); e++)
{
if(edges[e][0]==tp) next=edges[e][1];
else if(edges[e][1]==tp) next=edges[e][0];
if(next!=INT_MAX && !vis[next]) q.push_back(next);
}
}
}
return ans;
}
vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges)
{
int mindis = INT_MAX;
vector<int> ans;
for(int i=0; i<n; i++)
{
int dis = bfs(i, n, edges);
if(dis==mindis)
ans.push_back(i);
else if(dis<mindis)
{
mindis = dis;
ans.erase(ans.begin(), ans.end());
ans.push_back(i);
}
}
return ans;
}
};
反向bfs
正确的思路是:由分析可以知道最后的答案一定是一个或者两个节点,那么可以使用 “反向的bfs”,即不从起点,而是从叶子节点开始访问
- 每次遍历访问叶子节点(相当于每次都删除叶子节点)直到最后节点数少于3
最后剩下的未被访问的节点就是我们要找的答案了,相比于枚举起点的正向bfs,这种反向的bfs更适合起点与答案有关的场合
正向bfs复杂度为
O(n*n*e)
,其中一个n是枚举起点,另一个n是一次bfs中访问的n个节点,e是查找邻接关系需要的时间
反向bfs的复杂度为
O(n*e)
,因为不用枚举起点所以快了n倍
代码
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges)
{
vector<bool> vis(n, false);
int CNT = n;
while(CNT>2)
{
// 在未被访问的集合中且出现次数为1的节点是叶子节点
vector<int> cnt(n, 0);
for(int e=0; e<edges.size(); e++)
{
if(!vis[edges[e][0]]&&!vis[edges[e][1]])
{
cnt[edges[e][0]]++;
cnt[edges[e][1]]++;
}
}
for(int i=0; i<n; i++)
if(cnt[i]==1) {vis[i]=true; CNT--;}
}
vector<int> ans;
for(int i=0; i<n; i++)
if(!vis[i]) ans.push_back(i);
return ans;
}
};