PAT 甲级1004 Counting Leaves

PAT 甲级1004 Counting Leaves

N为树中节点数量0 < N < 100,M为叶子节点数量。

ID是代表非叶子节点的两位数(有child),K是其儿子节点数量, 后跟k个儿子节点ID

要按层输出每层叶子节点的数量

就是对树进行搜索

  1. 深度优先搜索DFS

    按深度优先对树进行搜索,每当遇到一个节点,如果该节点是叶子节点,则将该层的叶子节点数量++,同时比较该层深度与已知最大深度,如果该层深度更深,更新最大深度。

    如果该节点不是叶子节点,则对该节点的每个叶子节点,递归调用本函数。这样从根节点出发,将一直向更深处搜索,直到遇到一个叶子节点,此时返回该叶子节点的父节点,搜索该父节点的下一个儿子节点,如果没有下一个儿子节点,则继续返回。

    这个算法在什么时候结束呢?在遍历完根节点的所有儿子节点后(或者该根节点本身就是叶子节点)

    res< int> res[100]保存N个向量,res[i]保存节点i的儿子节点的信息

    book[100]:book[i]记录第i层的叶子节点数,因为0 < N < 100,所以该树必定小于100层。

    // 1004 Counting Leaves.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
    //
    
    #include <iostream>
    #include <vector>
    using namespace std;
    vector<int> res[100];
    int book[100], maxdepth = -1;
    void dfs(int index, int depth) {
        if (res[index].size() == 0) {
            book[depth]++;
            maxdepth = max(depth, maxdepth);
            return;
        }
        else {
            for (int i = 0; i < res[index].size(); i++) {
                dfs(res[index][i], depth + 1);
            }
        }
    }
    int main()
    {
        int n, m, node, k, c;
        cin >> n >> m;
        for (int i = 0; i < m; i++) {
            cin >> node >> k;
            for (int j = 0; j < k; j++) {
                cin >> c;
                res[node].push_back(c);
            }
        }
        dfs(1, 0);
        for (int i = 0; i <= maxdepth; i++) {
            if (i != 0) cout << " ";
            cout << book[i];
        }
        return 0;
    }
    
  2. 广度优先搜索

    层序遍历,首先将根节点加入队列,然后只要队列不为空就进行循环,每次取出队首节点,如果该节点无儿子节点,对应book[depth]++,如果有儿子节点,则将其儿子节点逐个加入队列中,记录此时队列中最后一个元素。当取出的元素等于记录的最后一个元素时,说明一层遍历完,进入下一层,更新最后一个元素的记录。

void bfs(int index, int depth) {
    int root = index;
    q.push(root);
    while (q.size()) {
        int v = q.front(), p = -1;
        q.pop();
        if (res[v].size() == 0) {
            book[depth]++;
            maxdepth = max(depth, maxdepth);
        }
        else {
            for (int i = 0; i < res[v].size(); i++) {
                q.push(res[v][i]);
                p = res[v][i];
            }
        }
        if (v == root) {
            depth++;
            root = p;
        }
    }
}

这种写法的bfs无法通过测试点2,麻了,还想不明白为啥

更新,给一个输入用例说明:

12
5
01 3 02 03 04
02 2 05 06
03 2 07 08
05 2 09 10
06 2 11 12

在这里插入图片描述

问题在于变量p定义在循环之内,当v指向3时,将3的儿子节点7、8加入队列,此时p更新为8,然后继续循环,v指向4,**此时p被循环内赋值为-1,**可知4是第二层的最后一个节点,此时应该进入:

if (v == root) {
            depth++;
            root = p;
        }

的循环中,可以知道,虽然进入了下一层,但root被赋值为-1,而不是我们想要的下一层的最末一个节点8(它被覆盖了)。

所以当我们进入第三层时,当v最终指向8时,因为root为-1,并不能进入上面这个循环,层数无法递增,所以depth保持在第三层的情况下,我们继续遍历队列中剩余的几个第四层节点,对它的叶子节点计数加到了第三层上。

book[depth]++;//depth = 3

因此最后的输出为

0 1 6
而不是
0 1 2 4

更正这种写法:

void bfs1(int index, int depth) {
    int root = index;
    q.push(root);
    int p = -1;
    while (q.size()) {
        int v = q.front();
        q.pop();
        if (res[v].size() == 0) {
            book[depth]++;
            maxdepth = max(depth, maxdepth);
        }
        else {
            for (int i = 0; i < res[v].size(); i++) {
                q.push(res[v][i]);
                p = res[v][i];
            }
        }
        if (v == root) {
            depth++;
            root = p;
        }
    }
}

另一种写法,参考尹神,以后bfs都按照这种写法,比较好理解

void bfs(int index, int depth) {
    int root = index; 
    q.push(root);
    q.push(-1);
    while (q.size()) {
        if (q.front() == -1) {
            q.pop();
            depth++;
            if (!q.empty()) q.push(-1);
        }
        else {
            int v = q.front();
            q.pop();
            if (res[v].size() == 0) {
                book[depth]++;
                maxdepth = max(depth, maxdepth);
            }
            else {
                for (int i = 0; i < res[v].size(); i++) {
                    q.push(res[v][i]);                  
                }
            }
        }
        
    }
}

向队列中插入-1作为一行的终结符,如果取出的队首元素为-1且队列空,则说明取完了,不要再插入-1,否则就再插入-1作为下一行的终结符。

可通过测试点2,以后我想明白为啥再来更新。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值