Leetcode Algorithm 310. Minimum Height Trees
Minimum Height Trees
给定一个具有树的特性的无向图,我们可以指定任意节点作为这棵树的根,其中有一些节点形成的树的高度是当中最短的,找出这样的所有节点
解题思路
先看看题目给出的样例1:
n = 4
,edges = [[1, 0], [1, 2], [1, 3]]
0
|
1
/ \
2 3
样例2:
n = 6
,edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]
0 1 2
\ | /
3
|
4
|
5
要使得所选的根节点形成的树的高度尽量小,根的左子树高度与右子树的高度相差要尽量少,那么该节点必然位于图的一条最长路径的中点处,才能使得左右子树尽量平衡。所以,当该路径的节点总数是奇数时,只有一个最优的根节点,偶数时则有两个。
但是,直接去找最长路径确是一件不容易的事情,我们可以换一种方法来确定中点——剪叶子。我们逐层把所有的叶子节点剪掉,剩下最后的节点就是我们想要的中点,因为这样可以从外围一步一步逼近中点,也保证了最长路径上的中点两边的节点是最晚剪掉的。
代码
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<pair<int, int> >& edges) {
vector<int> result;
// single node
if (n == 1) {
result.push_back(0);
}
// two nodes
else if (n == 2) {
result.push_back(0);
result.push_back(1);
}
// more than two nodes -> cut leaves
else if (n > 2) {
int degree[n] = { 0 };
int edgeSize = edges.size();
vector<vector<int> > graph(n);
queue<pair<int, int> > leaves;
// construct graph
for (int i = 0; i < edgeSize; i++) {
int pre = edges[i].first;
int post = edges[i].second;
graph[pre].push_back(post);
graph[post].push_back(pre);
}
// count the degrees and leaves
for (int i = 0; i < n; i++) {
degree[i] = graph[i].size();
if (degree[i] == 1) {
leaves.push(make_pair(i, 1));
}
}
int popCount = 0; // count the removed nodes
while (popCount != n - 2) {
int node = leaves.front().first;
int level = leaves.front().second;
for (int j = 0; j < graph[node].size(); j++) {
int neighbour = graph[node][j];
if (degree[neighbour] > 1) {
degree[neighbour]--;
if (degree[neighbour] == 1) {
leaves.push(make_pair(neighbour, level + 1));
}
}
}
leaves.pop();
popCount++;
}
// judge if the left two nodes are on the same level
int level_front = leaves.front().second;
int level_back = leaves.back().second;
if (level_front == level_back) {
result.push_back(leaves.front().first);
result.push_back(leaves.back().first);
sort(result.begin(), result.end());
} else if (level_front < level_back) {
result.push_back(leaves.back().first);
}
}
return result;
}
};
测试样例与输出
struct TestCase {
int n;
vector<pair<int, int> > edges;
};
int main() {
int numTestCase = 2;
TestCase tCase[numTestCase];
tCase[0].n = 4;
tCase[0].edges.push_back(make_pair(1, 0));
tCase[0].edges.push_back(make_pair(1, 2));
tCase[0].edges.push_back(make_pair(1, 3));
tCase[1].n = 6;
tCase[1].edges.push_back(make_pair(0, 3));
tCase[1].edges.push_back(make_pair(1, 3));
tCase[1].edges.push_back(make_pair(2, 3));
tCase[1].edges.push_back(make_pair(4, 3));
tCase[1].edges.push_back(make_pair(5, 4));
Solution s;
for (int t = 0; t < numTestCase; t++) {
vector<int> roots = s.findMinHeightTrees(tCase[t].n, tCase[t].edges);
cout << "[";
for (int i = 0; i < roots.size(); i++) {
if (i != roots.size() - 1)
cout << roots[i] << ", ";
else
cout << roots[i] << "]" << endl;
}
}
return 0;
}
输出
[1]
[3, 4]
知识点
树
树是一个没有环的连通的无向图,节点之间存在层次关系,每一层的节点只能与上一层的一个节点相连,但可以与下一层的多个节点相连。
树的性质
- 一颗有
n
的节点的树,会有n-1
条边 - 任意连通的无向图
G=(V, E)
,如果|E|=|V|-1
成立,那么这个图是一棵树。 - 一个无向图是树的充要条件是任意两点之间有且只有一条通路。