力扣练习——49 最小高度树

1.问题描述

对于一个具有树特征的无向图,我们可选择任何一个节点作为根。图因此可以成为树,在所有可能的树中,具有最小高度的树被称为最小高度树。给出这样的一个图,写出一个函数找到所有的最小高度树并返回他们的根节点。

该图包含 n 个节点,标记为 0 到 n - 1。给定数字 n 和一个无向边 edges 列表(每一个边都是一对标签)。

你可以假设没有重复的边会出现在 edges 中。由于所有的边都是无向边, [0, 1]和 [1, 0] 是相同的,因此不会同时出现在 edges 里。

 

示例 1:

n = 4, edges = [[1, 0], [1, 2], [1, 3]]

        0

        |

        1

       / \

      2   3 

输入: 

4

1  0

1  2

1  3

输出: 1

 

示例 2:

n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]]

     0  1  2

      \ | /

        3

        |

        4

        |

        5 

输入: 

6

0  3

1  3

2  3

4  3

5  4

输出: 3 4

说明:

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

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

 

可使用以下main函数:

int main()

{

    int n;

    vector<vector<int> > edges;

    cin>>n;

    int p1,p2;

    for(int i=0; i<n-1; i++)//边的数目为节点数目减1

    {

        cin>>p1>>p2;

        vector<int> edge;

        edge.push_back(p1);

        edge.push_back(p2);

        edges.push_back(edge);

    }

    vector<int> res=Solution().findMinHeightTrees(n, edges);

    sort(res.begin(), res.end());

    for(int i=0; i<res.size(); i++)

    cout<<res[i]<<" ";

}

 

2.输入说明

首先输入节点的个数n,

然后输入n-1行(n个节点存在n-1条边),每行两个整数ai,、bi,表示节点ai和节点bi之间存在一条边,ai和bi不相同。

n<=20000

0 <= ai, bi < n

ai != bi

所有 (ai, bi) 互不相同

给定的输入 保证 是一棵树,并且 不会有重复的边

3.输出说明

对于所有找到的根节点,按照编号从小到大的顺序输出,每个编号后跟一个空格。

4.范例

输入

6
0 3
1 3
2 3
4 3
5 4

输出

3 4

5.代码

#include <iostream>
#include <queue>
#include <cstdlib>
#include <cstring>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;

vector<int> findMinHeightTrees(int n, vector<vector<int> > edges)
{
    //树的高度是指根节点和叶子节点之间最长向下路径上边的数量,要求输出最小高度树的根节点
    //对于所有找到的根节点,按照编号从小到大的顺序输出,每个编号后跟一个空格。
    //输入n,则总共n个节点,编号从0到n-1
    //参考网址:https://leetcode.cn/problems/minimum-height-trees/solution/zui-rong-yi-li-jie-de-bfsfen-xi-jian-dan-zhu-shi-x/

    if (n == 1)
        return { 0 };//只有一个节点,编号为0,该节点就是满足的根节点
    vector<int>degrees(n);//总共n个节点,下标从0到n-1,分别代表图上面的点
    vector<int>res;//定义结果
    map<int, vector<int>>mm;//定义邻接表
    //记录邻接表
    for (int i = 0; i < edges.size(); i++)
    {
        //因为是无向图,所以邻接表需要分别记录起点和终点的邻接情况
        int u = edges[i][0];//边起点
        int v = edges[i][1];//边终点
        degrees[u]++;//该节点的出现次数加1
        degrees[v]++;
        mm[u].push_back(v);//u和v是邻接点,有边相连
        mm[v].push_back(u);
    }
    //将叶子节点先入队
    queue<int>q;
    for (int i = 0; i < n; i++)
    {
        if (degrees[i] == 1)//只出现一次,说明是叶子节点
            q.push(i);
    }
    //BFS过程
    //从叶子节点开始朝内部遍历,寻找两边同时从中间靠拢的节点,将圈子一圈圈缩小
    //这里对示例的理解要透彻,一个无向图,所有当前的出度为0的节点【相当于叶子节点,也就是现在队列中保存的】都进行依次遍历,然后将其删去,得到新的图【原图缩小】,将叶子节点重新加入
    //剩下的最后一次保留的叶子节点就是所要求的树的根节点
    while (!q.empty())
    {
        res.clear();//每层遍历结束都要清空结果
        int size = q.size();
        for (int i = 0; i < size; i++)
        {
            int num = q.front();//头节点
            q.pop();//出队列,类似于删除已经遍历的结点
            res.push_back(num);//加入结果中
            degrees[num]--;//更新
            //对当前节点的邻接点进行访问
            for (auto j : mm[num])//访问map中与之相邻的结点
            {
                degrees[j]--;//在删除更新
                if (degrees[j] == 1)//若删除前一个结点后,与之相邻的结点变成叶子节点,就入队
                    q.push(j);//注意,这里是新一轮BFS的起点
            }
        }

    }

    return res;//返回最后一次保留的叶子节点,这里的res是无序的,后面输出的时候用了sort(),才实现题目要求的从小到大输出

}

int main()

{

    int n;

    vector<vector<int> > edges;

    cin >> n;

    int p1, p2;

    for (int i = 0; i < n - 1; i++)//边的数目为节点数目减1

    {

        cin >> p1 >> p2;

        vector<int> edge;

        edge.push_back(p1);

        edge.push_back(p2);

        edges.push_back(edge);

    }

    vector<int> res = findMinHeightTrees(n, edges);//注:这里返回的vector是无序的

    sort(res.begin(), res.end());//先进行排序,然后再输出

    for (int i = 0; i < res.size(); i++)

        cout << res[i] << " ";

}

 

posted @ 2022-08-03 18:39  努力奋斗的小企鹅  阅读(76)  评论(0)    收藏  举报