0
点赞
收藏
分享

微信扫一扫

LeetCode-310. 最小高度树【dfs,bfs,拓扑排序】

题目描述:

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

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

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

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

树的 高度 是指根节点和叶子节点之间最长向下路径上边的数量。在这里插入图片描述

解题思路一:拓扑排序(无向图)

结论:逐层删除叶子节点和边,剩下的最后小于等于2个节点即是根节点
1.首先将所有叶子节点压入队列,此时令节点剩余计数 remainNodes=n;
2.同时将当前 remainNodes 计数减去叶子节点数目,将当前队列中叶子节点取出,并将与之相邻的节点的度减少1,重复上述步骤将当前节点中度为 1 的节点压入队列中;
3.重复上述步骤,直到剩余的节点数组 remainNodes≤2 时,此时剩余的节点即为当前高度最小树的根节点。
emplace=push但是二者有一些区别。emplace要新和高效一些。

class Solution {
public:
	//拓扑排序实现要点:1.记录当前节点的相邻节点。2.每个节点的度。3.队列实现(度为1入队)
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        if(n==1){//如果无向图只有一个节点可直接返回{0};
            return {0};//返回值类型是vector<int>
        }
        vector<int> degree(n);
        vector<vector<int>> adj(n);//n个节点
        for(auto &edge: edges){//要拓扑排序必须找到每个节点相邻的节点和每个节点的度
            adj[edge[0]].emplace_back(edge[1]);
            adj[edge[1]].emplace_back(edge[0]);
            ++degree[edge[0]];
            ++degree[edge[1]];
        }
        queue<int> qu;//队列实现拓扑排序
        vector<int> ans;//记录最终根节点
        for(int i=0;i<n;++i){//队列初始化
            if(degree[i]==1){//叶子节点入队
                qu.emplace(i);
            }
        }
        int remainNodes=n;
        while(remainNodes>2){
            int sz=qu.size();
            remainNodes-=sz;//减去叶子节点数
            for(int i=0;i<sz;++i){
                int curr=qu.front();//取队首节点
                qu.pop();
                for(auto &v: adj[curr]){//取当前节点的相邻节点
                    if(--degree[v]==1){//节点v度先减1若减去之后v的度为1则入队.
                        qu.emplace(v);
                    }
                }
            }
        }
        while(!qu.empty()){
            ans.emplace_back(qu.front());
            qu.pop();
        }
        return ans;
    }
};

在这里插入图片描述
emplace操作是C++11新特性,新引入的的三个成员emplace_front、emplace 和 emplace_back。这些操作构造而不是拷贝元素到容器中,这些操作分别对应push_front、insert 和push_back,允许我们将元素放在容器头部、一个指定的位置和容器尾部。

两者的区别 :
当调用insert时,是将对象传递给insert,对象被拷贝到容器中,而当我们使用emplace时,是将参数传递给构造函,emplace使用这些参数在容器管理的内存空间中直接构造元素。
复杂度分析
时间复杂度:O(n),其中 n 是为节点的个数。图中边的个数为 n−1,因此建立图的关系需要的时间复杂度为 O(n),通过广度优先搜索需要的时间复杂度为 O(n+n−1),求最长路径的时间复杂度为 O(n),因此总的时间复杂度为 O(n)。

空间复杂度:O(n),其中 n 是节点的个数。由于题目给定的图中任何两个顶点都只有一条路径连接,因此图中边的数目刚好等于 n−1,用邻接表构造图所需的空间刚好为 O(2×n),存储每个节点的距离和父节点均为 O(n),使用广度优先搜索时,队列中最多有 n 个元素,所需的空间也为 O(n),因此空间复杂度为 O(n)。

解题思路二:广度优先搜索BFS(看注释)

结论:找最长的一条路径
1.从节点0开始找最远节点x
2.从节点x开始找最远节点y
3.x-y即是最远路径

class Solution {
public:
	//BFS算法要点:1.记录当前节点的相邻节点。2.访问数组(每个节点仅访问一次)3.队列实现(未访问入队)
    int findLongestNode(int u, vector<int> & parent, vector<vector<int>>& adj) {
    //parent记录从u节点开始每个节点的父节点,BFS算法
        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;//记录队列中广度搜索最后一个节点,即是距离节点u最远的节点。
            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);
        /* 找到与节点 0 最远的节点 x */
        int x = findLongestNode(0, parent, adj);
        /* 找到与节点 x 最远的节点 y */
        int y = findLongestNode(x, parent, adj);
        /* 求出节点 x 到节点 y 的路径 */
        vector<int> path;//记录一条从x到y的路径
        parent[x] = -1;//parent此时记录的是x开始广度搜索之后每个节点的父节点,可想而知x是父节y是最后一个子节点,即一条从x到y的路径。
        while (y != -1) {
            path.emplace_back(y);
            y = parent[y];
        }
        int m = path.size();
        if (m % 2 == 0) {//如果m为偶数,此时最小高度树的根节点为{path[m / 2 - 1], path[m / 2]}
            return {path[m / 2 - 1], path[m / 2]};
        } else {//如果m为奇数,此时最小高度树的根节点为 {path[m / 2]}
            return {path[m / 2]};
        }//这是结论。证明可以看链接:https://leetcode-cn.com/problems/minimum-height-trees/solution/zui-xiao-gao-du-shu-by-leetcode-solution-6v6f/
    }
};

解题思路三:深度优先搜索DFS(递归传送的参数可以稍微注意一下)

和广度优先搜索差不多

class Solution {
public:
	//DFS算法要点:1.记录当前节点的相邻节点。2.递归实现(若dist不为-1则深度搜索一遍。)即记录每个节点到当前节点的深度。
    void dfs(int u, vector<int> & dist, vector<int> & parent, const vector<vector<int>> & adj) {
        for (auto & v : adj[u]) {
            if (dist[v] < 0) {
                dist[v] = dist[u] + 1;
                parent[v] = u;
                dfs(v, dist, parent, adj); 
            }
        }
    }

    int findLongestNode(int u, vector<int> & parent, const vector<vector<int>> & adj) {
        int n = adj.size();
        vector<int> dist(n, -1);
        dist[u] = 0;
        dfs(u, dist, parent, adj);
        int maxdist = 0;
        int node = -1;
        for (int i = 0; i < n; i++) {
            if (dist[i] > maxdist) {
                maxdist = dist[i];
                node = i;
            }
        }
        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);
        /* 找到距离节点 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]};
        }
    }
};
举报

相关推荐

0 条评论