310. 最小高度树(LeetCode)

这里只是给出BFS的算法,还有官方题解中未解释的部分(为什么某一个节点的最远节点x,x的最远节点y,(x,y)的路径为什么是最长路径?)

树是一个无向图,其中任何两个顶点只通过一条路径连接。 换句话说,一个任何没有简单环路的连通图都是一棵树。

给你一棵包含 n 个节点的树,标记为 0 到 n - 1 。给定数字 n 和一个有 n - 1 条无向边的 edges 列表(每一个边都是一对标签),其中 edges[i] = [ai, bi] 表示树中节点 aibi 之间存在一条无向边。

可选择树中任何一个节点作为根。当选择节点 x 作为根节点时,设结果树的高度为 h 。在所有可能的树中,具有最小高度的树(即,min(h))被称为 最小高度树

请你找到所有的 最小高度树 并按 任意顺序 返回它们的根节点标签列表。

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。

示例 1:

输入:n = 4, edges = [[1,0],[1,2],[1,3]]
输出:[1]
解释:如图所示,当根是标签为 1 的节点时,树的高度是 1 ,这是唯一的最小高度树。

示例 2:

输入:n = 6, edges = [[3,0],[3,1],[3,2],[3,4],[5,4]]
输出:[3,4]

提示:

  • 1 <= n <= 2 * 104
  • edges.length == n - 1
  • 0 <= ai, bi < n
  • ai != bi
  • 所有 (ai, bi) 互不相同
  • 给定的输入 保证 是一棵树,并且 不会有重复的边
//
// Created by py on 24-3-17.
//
#include <iostream>
#include <vector>
#include <queue>
#include <unordered_map>
#include <deque>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>

using namespace std;
class Solution {
public:
    int findLongestNode(int u, vector<int> &parent, vector<vector<int>> &adj) {
        int n = adj.size();
        queue<int> qu;
        vector<bool> visit(n);
        qu.emplace(u);
        visit[u] = true;
        int node = -1;

        while (!qu.empty()) {
            int curr = qu.front();
            qu.pop();
            node = curr;
            for (auto &v: adj[curr]) {
                if (!visit[v]) {
                    visit[v] = true;
                    parent[v] = curr;
                    qu.emplace(v);
                }
            }
        }
        return node;
    }

    vector<int> findMinHeightTrees(int n, vector<vector<int>> edges) {
        //特殊情况
        if (n == 1) {
            return {0};
        }
        //构建邻接表
        vector<vector<int>> adj(n);
        for (auto &edge: edges) {
            adj[edge[0]].emplace_back(edge[1]);
            adj[edge[1]].emplace_back(edge[0]);
        }

        vector<int> parent(n, -1);
        /* 本处有一个疑问:为什么要求最长路径 ?请看
         * https://leetcode.cn/problems/minimum-height-trees/solutions/1395249/zui-xiao-gao-du-shu-by-leetcode-solution-6v6f/
         * 在某一条无向无环路中,任意一个节点的最远节点为x,x的最远节点为y,这条路的最长的路即为:x,y
         * 可证明:设有一无向无环图(有向的话清参考拓扑排序算法),取任意节点 x
         *        利用贪心思想 , 寻找节点 x 最长的路,该路必为叶子节点 y,无子节点。
         *                      反证法证明上述:假如不为叶子节点,最远距离 dis[x][y]
         *                                   又因 y 存在叶子节点 z ,存在 dis[x][z] > dis[x][y]与最长矛盾
         *        又因为是叶子节点 y ,寻找其最远的节点 z ,z也比为某一个叶子节点(上述已然证明)
         *        证明: dis[y][z] = maxNode
         *              dis[x][y] 为 x 的最长路径
         *              dis[y][z] 为 z 的最长路径
         *              本处又利用反证法:假设存在 两个节点 node1,node2 dis[node1][node2] > dis[y][z]
         *                             设任意节点 a , 都存在:由上式 (dis[node1][node2] > dis[y][z])
         *                             有dis[node1][a] + dis[a][node2] > dis[y][a] + dis[a][z]
         *                             但又与前边的 假设 x 节点违背,故矛盾
         *        最后得出:dis[y][z] = maxNode
         * */
        /* 找到与节点 0 最远的节点 x */
        int x = findLongestNode(0, parent, adj);
        /* 找到与节点 x 最远的节点 y */
        int y = findLongestNode(x, parent, adj);
        /* 求出节点 x 到节点 y 的路径 */
        vector<int> path;
        parent[x] = -1;
        while (y != -1) {
            path.emplace_back(y);
            y = parent[y];
        }
        int m = path.size();
        if (m % 2 == 0) {
            return {path[m / 2 - 1], path[m / 2]};
        } else {
            return {path[m / 2]};
        }
    }
};


int main() {
    Solution solution;
    vector<int> trees = solution.findMinHeightTrees(6, {{0, 1},
                                                        {0, 2},
                                                        {0, 3},
                                                        {3, 4},
                                                        {5, 4}});
    for (const auto &item: trees) {
        cout << item << " ";
    }
    return 0;
}

 

  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值