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]
理解题意:题目的意思是给定一个无向连接图,从中选取一个节点作为root,看做一颗树,返回最小高度的树节点。
当只有一个节点的时候,即n==1,那么就可以直接返回;
当只有两个节点的时候,即n==2,那么这两个节点都是符合条件的,任意一个作为root节点,高度都是一样的,所以需要返回这两个节点。
当n>2时候,必然就会存在叶子节点和中间节点的区别,所谓的叶子节点就是只有一个节点与之连接,中间节点就是至少存在两个节点与之相连接。如果选取叶子节点为root,这样的树高度为height = 1+height(中间节点)。很显然,得到的树高度会大于中间节点。所以按照题意,我们只需要选取中间节点,然后遍历,深度搜索,就能够获得最小高度树的root节点,线性复杂度。
不过可以根据图论的一些知识,来简化本题的解法。逐步的删除叶子节点,最后剩余的一个或者两就是答案。这里应该说明的问题的是,符合条件的节点最多只有两个,为什么呢?
假设存在三个点,说明以这三个点为root的树高度是一样的,就是到达最远叶子节点的路径长度是一样的,现在我们逐步删除叶子节点,这样三个点为root的树高度应该逐步减1,最后如果高度都减为0,说明有三个根节点也就是三棵树,这三个节点是独立的,违背了树的定义。下面给出C++实现代码:
struct Node{
unordered_set<int> neighbor;
bool isLeaf()const{return neighbor.size()==1;}
};
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector<int> pB1;
vector<int> pB2;
if(n==1){
pB1.push_back(0);
return pB1;
}
if(n==2){
pB1.push_back(0);
pB1.push_back(1);
return pB1;
}
// build the graph
vector<Node> nodes(n);
for(auto p:edges){
nodes[p.first].neighbor.insert(p.second);
nodes[p.second].neighbor.insert(p.first);
}
// find all leaves
for(int i=0; i<n; ++i){
if(nodes[i].isLeaf())
pB1.push_back(i);
}
// remove leaves layer-by-layer
while(true){
for(int i : pB1){
//下面这个循环体应该只会执行一次,因为是叶子节点,只有一个邻接节点
for(auto j: nodes[i].neighbor){
//删除叶子节点,同时记录因此产生的新一轮的叶子节点
nodes[j].neighbor.erase(i);
if(nodes[j].isLeaf())
pB2.push_back(j);
}
}
if(pB2.empty()){
return pB1;
}
pB1.clear();
swap(pB1, pB2);
}
}