想要找到图里面的MHT,就要找出最长路径,最长路径的中点就是MHT的根,当最长路径的树节点个数是奇数的时候,MHT只有一棵,当最长路径的树节点个数是偶数的时候,MHT有两棵。找出无向连通图里面的直径,直径的中点就是所求的根,找出直径的方法使用两次BFS,第二次BFS还要记录下,路径中每个节点的父节点,第一次随意选择一个节点做BFS所能到达的最长路径的端点肯定是直径的一端,第二次再从这个直径的一端出发就能找出直径的另外一个端点。参考文章(证明过程可以看里面):
http://wattlebird.github.io/2014/09/21/%E6%A0%91%E7%9A%84%E7%9B%B4%E5%BE%84/
class Solution {
public:
vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) {
unordered_map<int, vector<int>> hashTable;
for (int i = 0; i < edges.size(); i++) {
hashTable[edges[i].first].push_back(edges[i].second);
hashTable[edges[i].second].push_back(edges[i].first);
}
const int lenArrSize = n;
int lenArray[lenArrSize];
bool visited[lenArrSize];
for (int i = 0; i < lenArrSize; i++) {
lenArray[i] = 0;
visited[i] = false;
}
queue<int> BFSQueue;
BFSQueue.push(0);
int maxLen = 0, endNode = 0;
while (!BFSQueue.empty()) {
int currentNode = BFSQueue.front();
BFSQueue.pop();
visited[currentNode] = true;
for (int i = 0; i < hashTable[currentNode].size(); i++) {
if (visited[hashTable[currentNode][i]]) {
continue;
}
lenArray[hashTable[currentNode][i]] = lenArray[currentNode] + 1;
if (lenArray[hashTable[currentNode][i]] > maxLen) {
endNode = hashTable[currentNode][i];
maxLen = lenArray[hashTable[currentNode][i]];
}
BFSQueue.push(hashTable[currentNode][i]);
}
}
for (int i = 0; i < lenArrSize; i++) {
lenArray[i] = 0;
visited[i] = false;
}
BFSQueue.push(endNode);
maxLen = 0;
int nextNode = endNode;
int lastNodeArray[lenArrSize];
while (!BFSQueue.empty()) {
int currentNode = BFSQueue.front();
BFSQueue.pop();
visited[currentNode] = true;
for (int i = 0; i < hashTable[currentNode].size(); i++) {
if (visited[hashTable[currentNode][i]]) {
continue;
}
lenArray[hashTable[currentNode][i]] = lenArray[currentNode] + 1;
lastNodeArray[hashTable[currentNode][i]] = currentNode;
if (lenArray[hashTable[currentNode][i]] > maxLen) {
nextNode = hashTable[currentNode][i];
maxLen = lenArray[hashTable[currentNode][i]];
}
BFSQueue.push(hashTable[currentNode][i]);
}
}
for (int i = 0; i < lenArrSize; i++) {
cout << lastNodeArray[i] << " " << endl;
}
cout << endl;
int iterNode = nextNode;
vector<int> nodePath;
nodePath.push_back(nextNode);
while (iterNode != endNode) {
nodePath.push_back(lastNodeArray[iterNode]);
iterNode = lastNodeArray[iterNode];
}
int midInd = (int(nodePath.size()) - 1) / 2;
if (nodePath.size() % 2 == 1) {
return {nodePath[midInd]};
} else {
return {nodePath[midInd], nodePath[midInd + 1]};
}
}
};
另一种思路,假设这个无向连通图就只有一条路径,那么,用两个指针,从这条路径的两端一直往中间遍历,遍历到最后它们相遇了或者它们只相差一个位置的时候所指向的节点就是根节点,再扩展一下,假设这条最长路径上面的节点还连着其他一些路径,但是这些路径的长度都没有最长路径长,用若干个指针从各条路径的端点开始遍历,最后这些指针都会听到最长路径的节点上,而最长路径的两个端点的指针还是在其他指针的外围,再将这两个指针按照在一条路径上收敛的方法,找出最后的根节点。在无向连通图上面,各条路径的端点实际上就是叶子节点,随着各个指针向中心收敛,最长路径之外的叶子节点都会消失,即不断剪枝,每次都是减掉所有的叶子节点,最后只会剩两个叶子节点继续剪枝,再继续剪枝,直到剩下一个节点或两个节点,就是所要求的根节点。
class Solution { public: vector<int> findMinHeightTrees(int n, vector<pair<int, int>>& edges) { vector<int> leafSet; unordered_map<int, set<int>> hashTable; for (int i = 0; i < edges.size(); i++) { hashTable[edges[i].first].insert(edges[i].second); hashTable[edges[i].second].insert(edges[i].first); } for (int i = 0; i < hashTable.size(); i++) { if (hashTable[i].size() == 1) { leafSet.push_back(i); } } while (leafSet.size() > 2 || (leafSet.size() == 2 && *hashTable[leafSet[0]].begin() != leafSet[1])) { vector<int> newLeafSet; for (int i = 0; i < leafSet.size(); i++) { hashTable[*hashTable[leafSet[i]].begin()].erase(leafSet[i]); if (hashTable[*hashTable[leafSet[i]].begin()].size() == 1) { newLeafSet.push_back(*hashTable[leafSet[i]].begin()); } hashTable[leafSet[0]].clear(); } leafSet = newLeafSet; } if (n == 1) { leafSet = {0}; } return leafSet; } };