scau 18924 二叉树的宽度

Description

二叉树的宽度指的是具有节点数目最多的那一层的节点个数。
          1
         / \
        2   3
       /     
      4     
答案为2, 第二层节点数最多,为2个节点。

输入格式

共n行。
第一行一个整数n,表示有n个结点,编号为1至n,结点1为树根。(1<=n<=50)
第二行至第n行,每行有两个整数x和y,表示在二叉树中x为y的父节点。x第一次出现时y为左孩子

输出格式

输出二叉树的宽度。

输入样例

5
1 2
1 3
2 4
2 5

输出样例

2

       这道题从周一的实验课到周二的一整个下午,前前后后花了差不多三个半小时,才勉勉强强搞懂……这道题看起来蛮简单的,因为之前有做过二叉树的右视图,思路是有的,但就是实现不了。我大抵是个笨蛋呜呜……

       在网上找了好多博主写的题解,emm,都看不太懂……

       反正都看不太懂,那就干脆只找一个,弄懂为止。本题的第一个方法,参考了下面这位博主的博客。做题的时候一定要先自己想,实在想不出来再来搜题解哦!搜到题解也不能直接Ctrl + C和Ctrl + V哦,就算是抄也要自已用手打一遍哦~https://blog.csdn.net/weixin_43004206/article/details/116243987?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165095906516782184698671%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165095906516782184698671&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-3-116243987.142^v9^control,157^v4^control&utm_term=18924+%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%AE%BD%E5%BA%A6&spm=1018.2226.3001.4187https://blog.csdn.net/weixin_43004206/article/details/116243987?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165095906516782184698671%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=165095906516782184698671&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-3-116243987.142%5Ev9%5Econtrol,157%5Ev4%5Econtrol&utm_term=18924+%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%AE%BD%E5%BA%A6&spm=1018.2226.3001.4187

        这个挨个演算一遍挺好懂的,我在这里浅浅做个笔记叭~

        首先,这个题的树吧,它的构建跟之前做的题所用的先序构建树不太一样,方法有很多。

struct node
{
    int parent;//父结点
    int lchild;//左孩子
    int rchild;//右孩子
} tree[55];

void CreatTree(int fu,int zi)
{
    tree[zi].parent=fu;//记录孩子结点的父结点,相当于一个前驱,可用来查找根结点
    if(!tree[fu].lchild)
        tree[fu].lchild=zi;
    else
        tree[fu].rchild=zi;
}

int main()
{
    int i,x,y,root;
    int MAX=0;
    cin >> n;
    for(i=1;i<n;i++)
    {
        cin >> x >> y;
        CreatTree(x,y);
    }
    ……
}

       我的方法如上,挺好理解的应该。输入x和y,x是y的父结点,且x第一次出现时y是左孩子(即左孩子优先)。详细看请CreatTree()函数。当然,对于这一道题来说,可以不用记录孩子结点的父结点,做这一步是为了后面找根结点,但题目已经说了1为根结点。

       然后用一个类似于dfs的算法来求深度。主函数中第一次递归调用kuandu(root,1),即从根结点、第一层开始。如果一个结点有左孩子,那么就进入到下一层,直到递归到一个叶子结点,然后返回,搜索上一个结点的右孩子。

void kuandu(int root,int a)
{
    level[a]++;//数组level用于记录每一层的个数
    //cout << a << ' '<< level[a] <<endl;
    int Root;
    if(tree[root].lchild)
    {
        Root=tree[root].lchild;
        kuandu(Root,a+1);
    }
    if(tree[root].rchild)
    {
        Root=tree[root].rchild;
        kuandu(Root,a+1);
    }
}

       第二个for循环是为了寻找根结点,上面说过,这道题已经说明1为根结点,所以第二个for循环可以不要,主函数中直接调用函数kuandu(1,1)即可。再是第三个for循环,找数组level[]的最大值。(Tips:我一开始写的是for(i=1;i<=55;i++),看出不同了吗?对,一开始是<=55,但是我的level[]数组下标是从0到54的,他到不了55,所以一直提交一直报错。也就是我们常说的数组溢出,蛮细节的,看了大半个小时我都没找到问题)

int main()
{
    int i,x,y,root;
    int MAX=0;
    cin >> n;
    for(i=1;i<n;i++)
    {
        cin >> x >> y;
        CreatTree(x,y);
    }
    for(i=1;i<=n;i++)
    {
        if(tree[i].parent==0)
        {
            root=i;
            break;
        }
    }
    kuandu(root,1);//从根结点、第一层开始
    for(i=1;i<55;i++)
    {
        MAX=max(level[i],MAX);
    }
    cout << MAX;
    return 0;
}

方法二——也就是标程(仅用了30行,看了之后愈发觉得我笨笨的)

       标程用了一个二维数组来存放这棵树。v[x][0]存放x的左孩子,v[x][1]存放x的右孩子。

    int n;
    cin >> n;
    int i;
    int x,y;
    for(i=1;i<n;i++)
    {
        cin >> x >> y;
        if(!v[x][0])
            v[x][0]=y;
        else
            v[x][1]=y;
    }

       题目有提示用队列的,但是一开始被循环绕晕了,我就没能继续写下去,因为第一层1个结点、第二层最多2个结点。第三层最多4个结点,第i层最多有2^(i-1)个结点,所以我不知道怎么控制循环条件。

       看了标程之后恍然大悟,这其实跟之前做过的一道题很像,没错,就是二叉树的右视图把父结点的孩子结点写进队列后,父结点出队,直到所有结点都出队,所以循环结束的条件就是q.size()!=0

       首先,定义一个变量len,来记录当前队列长度,也就是每一层的全部结点。注意,ans=max(len,ans)要写在内层循环前面。

       再来看看内层的for循环。首先,取队头元素(没有弹出),取完再弹出,然后判断其是否有左右孩子,有的话,孩子结点入队。前面讲的取完队头元素再弹出,意思就是每一次的for循环,上一层的结点都会全部出队,上一层结点的孩子结点会入队。

    queue<int>q;
    q.push(1);//根结点1入队
    while(q.size())
    {
        int len=q.size();
        ans=max(len,ans);
        for(i=0;i<len;i++)
        {
            int t=q.front();
            q.pop();//弹出队头元素
            if(v[t][0])
                q.push(v[t][0]);
            if(v[t][1])
                q.push(v[t][1]);
        }
    }

       STL的队列操作还是很方便哒,不会的同学可以自己去看看哦。这是我找的一个基本操作的博客。https://blog.csdn.net/sinat_40872274/article/details/81149852?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&utm_relevant_index=1icon-default.png?t=M3K6https://blog.csdn.net/sinat_40872274/article/details/81149852?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&utm_relevant_index=1

下面是两个方法的源代码。

方法一

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <malloc.h>
#include <queue>

using namespace std;

int n,level[55];

struct node
{
    int parent;//父结点
    int lchild;//左孩子
    int rchild;//右孩子
} tree[55];

void CreatTree(int fu,int zi)
{
    tree[zi].parent=fu;//记录孩子结点的父结点,相当于一个前驱,可用来查找根结点
    if(!tree[fu].lchild)
        tree[fu].lchild=zi;
    else
        tree[fu].rchild=zi;
}

void kuandu(int root,int a)
{
    level[a]++;//数组level用于记录每一层的个数
    //cout << a << ' '<< level[a] <<endl;
    int Root;
    if(tree[root].lchild)
    {
        Root=tree[root].lchild;
        kuandu(Root,a+1);
    }
    if(tree[root].rchild)
    {
        Root=tree[root].rchild;
        kuandu(Root,a+1);
    }
}

int main()
{
    int i,x,y,root;
    int MAX=0;
    cin >> n;
    for(i=1;i<n;i++)
    {
        cin >> x >> y;
        CreatTree(x,y);
    }
    for(i=1;i<=n;i++)
    {
        if(tree[i].parent==0)
        {
            root=i;
            break;
        }
    }
    kuandu(root,1);//从根结点、第一层开始
    for(i=1;i<55;i++)
    {
        MAX=max(level[i],MAX);
    }
    cout << MAX;
    return 0;
}

 方法二

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <malloc.h>
#include <queue>

using namespace std;

int n,v[105][2]={'\0'},ans=1;

int main()
{
    int n;
    cin >> n;
    int i;
    int x,y;
    for(i=1;i<n;i++)
    {
        cin >> x >> y;
        if(!v[x][0])
            v[x][0]=y;
        else
            v[x][1]=y;
    }
    queue<int>q;
    q.push(1);//根结点1入队
    while(q.size())
    {
        int len=q.size();
        ans=max(len,ans);
        for(i=0;i<len;i++)
        {
            int t=q.front();
            q.pop();//弹出队头元素
            if(v[t][0])
                q.push(v[t][0]);
            if(v[t][1])
                q.push(v[t][1]);
        }
    }
    cout << ans;
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值