【PAT甲级 树/图的遍历】1004 Counting Leaves (30分)

需要二刷
2021/2/21二刷,二刷题解已更新在最下面
来吐槽:
天哪我以前写的代码也太长了吧???不到40行一个DFS就写完的题目用了百来行?用的啥奇葩方法啊,看都看不懂。也太菜了吧???不过当初我还没学算法笔记,自己硬做的,现在看来也算是成长了吧。

第一次写的代码:

# include <iostream>
# include <vector>
# include <queue>
# include <map>

using namespace std;


/*
输入:
    树的结点个数(N) 树中的非叶子结点个数(M)
    
M行:
    非叶子结点的编号 非叶子结点的孩子的数量(K) 其K个孩子的编号 
    
    
输出:
    输出每一层的叶子结点的个数
*/
struct Node
{
    int father;
    bool haveChild;  // 该结点是否有孩子
    int level;       // 结点所在树的哪一层

    
    Node() = default;
    Node(int f, bool h, int l):father(f), haveChild(h), level(l){}
};

struct Tree
{
    vector<Node> nodes;  // 存放结点的数组
    int N;               // 总结点个数
    int M;               // 非叶子结点个数
    int depth;           // 这棵树的层数(深度)
    
    // 建立树的时候,以双亲表存储并计算出每一个节点所在层数和树的深度
    Tree()
    {
        int ID;         // 当前非叶子结点的ID(ID就是在nodes中的索引)
        int K;          // 当前非叶子结点的孩子个数
        int ID_child;   // 当前非叶子节点的孩子的ID
        cin >> N;
        if(N == 0)
            return;
        cin >> M;
            
        nodes = vector<Node>(N+1, {-1, false, 0});
        nodes[1].father = 0;
        nodes[1].level = 1;
        depth = nodes[1].level;
        
        for(int i=0;i<M;++i){
            cin >> ID >> K;
            for(int j=0;j<K;++j){
                cin >> ID_child;
                nodes[ID].haveChild = (K != 0);  // 如果孩子的数量大于0,设置为有孩子
                nodes[ID_child].father = ID;     // 设置子节点的父节点(一个孩子只有一个父亲)

                /*当输入的顺序是乱的时候,每个节点所在的层数无法正确地算出!!导致depth也无法算出,所以要单独在下面弄个循环单独算出
                nodes[ID_child].level = nodes[ID].level + 1;  // 子节点的所在层数是父节点所在层数+1
                depth = nodes[ID_child].level > depth ? nodes[ID_child].level : depth;  // 更新最大层数
                */
            }
        }

        // 单独算出每个结点的level和树的depth
        int cnt = 1;
        int j_baba;
        while(cnt != 0)
        {
            cnt=0;
            for(int j=1;j<=N;++j)
            {
                j_baba = nodes[j].father;
                // 如果父节点已经有所在层数但是子节点没有所在层数,就对子节点进行所在层数进行设置                    
                if(nodes[j_baba].level != 0 && nodes[j].level == 0){
                    nodes[j].level = nodes[j_baba].level + 1;
                    depth = nodes[j].level > depth ? nodes[j].level : depth;  // 更新最大层数
                }
                // 如果父节点和子节点都没有所在层数
                else
                if(nodes[j_baba].level == 0 && nodes[j].level == 0)
                    cnt = 1;
            }
        }
    }
    
    void printEveryLevelChildNum()
    {
        map<int, int> mp;
        
        // 广度优先遍历
        queue<int> Q;  // 建立一个队列存放结点的ID
        Q.push(1);     // 没有father的根节点先入队
        
        while(!Q.empty())
        {
            // 根节点出队
            int r = Q.front();
            Q.pop();
            
            // 访问根节点(如果当前节点有孩子,那么就让其所在层的叶子结点的数量+1)
            if(!nodes[r].haveChild)
                mp[nodes[r].level]++;
            
            // 子节点入队(在双亲表中看哪个节点的父节点是当前节点r,是的话它就是子节点,就将其入队)
            for(int i=1;i<=N;++i){
                if(nodes[i].father == r){
                    Q.push(i);
                }
            }
        }
        
        // 输出每一层的叶子结点的数量(depth是最大层数)
        for(int i=1;i<=depth;++i){
            cout << mp[i];
            if(i < depth)
                cout << " ";
        }cout << endl;
    }
    
    // 测试用,打印所有树节点的信息 
    void showTreeList(){
        printf("ID  father  haveChild  level\n");
        for(int i=1;i<=N;++i){
            printf("%d      %d        %d        %d\n", i, nodes[i].father, nodes[i].haveChild, nodes[i].level);
        }
    }
};



int main()
{
    Tree T;
//     T.showTreeList();
    T.printEveryLevelChildNum();
    
    return 0;
}


/*
10 6
01 2 02 03
02 2 04 05
03 1 06
05 1 07
06 2 08 09
08 1 10

*/

二刷AC代码:

# include <bits/stdc++.h>
using namespace std;
int N, M;
vector<int> child[101];
map<int, int> leaf; // 层数:叶子结点数的映射

int maxlevel = -1;
void DFS(int u, int level){
    if(child[u].size() == 0){
        leaf[level]++;
        maxlevel = max(maxlevel, level);
        return;
    }
    for(int v: child[u])
        DFS(v, level + 1);
}

int main(){
    cin >> N >> M;
    int u, K, v;
    for(int i = 0;i < M;++i){ // 输入非叶子结点的孩子们
        cin >> u >> K;
        for(int j = 0;j < K;++j){
            cin >> v;
            child[u].push_back(v);
        }
    }
    DFS(1, 0);
    for(int i = 0;i <= maxlevel;++i)
        cout << leaf[i] << (i == maxlevel ? "\n" : " ");
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值