版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jining11/article/details/83184573
题目要求
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.
翻译一下就是,给定n个节点和若干条边,把它画成一棵树,并且返回这样所有的节点——当把它们看作是这棵树的根时,这棵树的深度是最小的。
果然会超时的邻接表暴力法
#include <iostream>
#include <string>
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
//按层遍历获取树的高度
int get_height(int root, vector <vector <int>> & map, int n) {
int * flag = new int[n]();
queue <int> Q;
int label = 0;
Q.push(root);
while(!Q.empty()) {
int count = Q.size();
label++;
for (; count > 0; count--) {
int head = Q.front();
flag[head] = 1;
for (int p = 0; p < n; p++) {
if (map[head][p] && flag[p] == 0) {
Q.push(p);
}
}
Q.pop();
}
}
delete[] flag;
return label;
}
//返回最小的节点
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector <vector <int>> map(n, vector<int>(n,0));
for (auto & point: edges) {
map[point.first][point.second] = 1;
map[point.second][point.first] = 1;
}
int min_height = n;
int pre_height = 0;
vector <int> height;
for (int i = 0; i < n; i++) {
pre_height = get_height(i, map, n);
min_height = min_height > pre_height ? pre_height : min_height;
height.push_back(pre_height);
}
vector <int> result;
for (int i = 0; i < n; i++) {
if (height[i] == min_height) {
result.push_back(i);
}
}
return result;
}
};
int main() {
Solution s;
vector<pair<int, int>> edges;
edges.push_back(pair<int, int>(0,3));
edges.push_back(pair<int, int>(1,3));
edges.push_back(pair<int, int>(2,3));
edges.push_back(pair<int, int>(4,3));
edges.push_back(pair<int, int>(5,4));
vector<int> a = s.findMinHeightTrees(6, edges);
for (auto i: a) {
cout << i << endl;
}
return 0;
}
在节点数较小的情况下表现尚可,但是在最后几个例子里面还是超时了。
如果一定要说这个算法有什么亮点的话,那大概就是我第一次用vector快捷地实现了一个二维数组,以及在BFS算法的基础上改进了一点点以实现分层遍历。
这回居然还没过的链表暴力法
说真的,倒在最后一个样例上是最让人不爽的事情之一了。
——鲁迅
小改了一下算法,在图的最大度大于1的情况下,不再试图令度为1的节点也就是叶子结点成为根节点,顺带把邻接表换成了类似于链表的结构,但还是过不了最后的超大样例。难道是因为STL太慢了吗。。。
class Solution {
public:
//按层遍历获取树的高度
int get_height(int root, vector <vector <int>> & map, int n) {
int * flag = new int[n]();
queue <int> Q;
int label = 0;
Q.push(root);
while(!Q.empty()) {
int count = Q.size();
label++;
for (; count > 0; count--) {
int head = Q.front();
flag[head] = 1;
for (auto & p : map[head]) {
if (flag[p] == 0) {
Q.push(p);
flag[p] = 1;
}
}
Q.pop();
}
}
delete[] flag;
return label;
}
//返回最小的节点
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector <int> result;
//所有节点都是叶子结点有且只有这一种情况
if (n == 2) {
result.push_back(0);
result.push_back(1);
return result;
}
vector <vector <int>> map(n);
for (auto & point: edges) {
map[point.first].push_back(point.second);
map[point.second].push_back(point.first);
}
int min_height = n;
int pre_height = 0;
vector <int> height;
for (int i = 0; i < n; i++) {
if (map[i].size() == 1) {
height.push_back(-1);
continue;
}
pre_height = get_height(i, map, n);
min_height = min_height > pre_height ? pre_height : min_height;
height.push_back(pre_height);
}
for (int i = 0; i < n; i++) {
if (height[i] == min_height) {
result.push_back(i);
}
}
return result;
}
};
解法三:从入海口回溯三江源
这是我黔驴技穷之后看到大佬的思路豁然开朗做出来的,能想出这个解法真的令人惊叹。这道题最关键的一点在于,最终解只有一个或者是两个相互指向的节点,因为当有三个长度相同的根时,就必然会形成一个环。因此我们需要做的,仅仅是从叶子节点往上,层层回溯,直到只剩下两个或一个节点。
要留意的一个重要情况是,可能会有叶子节点直接指向根节点,如果此时就把根节点加入队列,根节点就会被踢出去,为此,还需要实时标记各个节点的度(这里的度是与尚未加入队列的节点建立起来的连接的数目),仅仅把那些度为1的节点加入队列。
class Solution {
public:
//返回最小的节点
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
vector <int> result;
vector <vector <int>> map(n);
//标记访问过的节点的总数
int visited = 0;
//标记是否访问某个节点
int * flag = new int[n]();
//标记各个节点的度数
int * degrees = new int[n]();
for (auto & point: edges) {
map[point.first].push_back(point.second);
map[point.second].push_back(point.first);
degrees[point.first]++;
degrees[point.second]++;
}
queue <int> Q;
for (int i = 0; i < n; i++) {
if (degrees[i] <= 1) {
Q.push(i);
flag[i] = 1;
visited++;
}
}
//第二种情况是有还没有访问到的节点,针对一个根节点两个叶子结点的情况
while(Q.size() > 2 || n > visited) {
int count = Q.size();
for (; count > 0; count--) {
int head = Q.front();
for (auto & p : map[head]) {
if (flag[p] == 1) {
continue;
}
degrees[p]--;
if (degrees[p] == 1) {
Q.push(p);
flag[p] = 1;
visited++;
}
}
Q.pop();
}
}
while(!Q.empty()) {
result.push_back(Q.front());
Q.pop();
}
delete [] flag;
delete [] degrees;
return result;
}
};