LeetCode題解:Minimum Height Trees
阿新 • • 發佈:2018-12-05
題目要求
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;
}
};