leetcode 310. Minimum Height Trees BFS,找无向连通图的直径

想要找到图里面的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;
    }
};





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值