PAT甲级 1004 Counting Leaves (30 分) 题解 DFS/BFS

1004 Counting Leaves (30 分)
Time limit: 400 ms
Memory limit: 64 MB
Source limit: 16 KB
A family hierarchy is usually presented by a pedigree tree. Your job is to count those family members who have no child.

Input

Each input file contains one test case. Each case starts with a line containing 0 < N < 100 0< N<100 0<N<100, the number of nodes in a tree, and M ( < N ) M (<N) M(<N), the number of non-leaf nodes. Then M M M lines follow, each in the format:

ID K ID[1] ID[2] ... ID[K]

where ID is a two-digit number representing a given non-leaf node, K is the number of its children, followed by a sequence of two-digit ID's of its children. For the sake of simplicity, let us fix the root ID to be 01.

Output

For each test case, you are supposed to count those family members who have no child for every seniority level starting from the root. The numbers must be printed in a line, separated by a space, and there must be no extra space at the end of each line.

The sample case represents a tree with only 2 nodes, where 01 is the root and 02 is its only child. Hence on the root 01 level, there is 0 leaf node; and on the next level, there is 1 leaf node. Then we should output 0 1 in a line.

Example

input
2 1
01 1 02
output
0 1

题意:

给定一棵以01为根结点的树,输出每一层的叶子节点个数。

注意事项:

这棵树可能只有根结点。

input
1 0
output
1

思路:

树的表示方法有两种。一种是使用双亲表示法,另一种是把它看成图使用邻接表表示~~(书上称这种表示方法为兄弟表示法)~~。

这里我分别使用两种不同的方式来做。

方法一、双亲表示法

利用双亲表示法很容易知道结点x的父亲结点是哪个结点,但是却无法知道结点x是否为叶子结点。不过我们可以使用一个bool数组hasChild来判断结点x是否为叶子结点。对于所有的叶子结点,利用dfs寻找根结点来求出根结点到叶子结点的距离。因为最后的输出,所以需要求出树的高度,这个可以在求根结点到叶子结点的距离的同时求出,并把它保存在变量maxHeight上。

出于效率的原因代码使用了数组d来保存结点x到根结点的距离,这里因为 N N N 的值较小,因此也可以不用数组d

使用数组d的时间复杂度: O ( N ) O(N) O(N), 不使用数组d的时间复杂度: O ( N 2 ) O(N^2) O(N2).

#include 
using namespace std;
const int MAXN = 100;
int fa[MAXN], d[MAXN], cnt[MAXN], height, n, m;
bool hasChild[MAXN];
int getDepth(int x) {
    if (d[x] != -1) return d[x];
    else return d[x] = getDepth(fa[x]) + 1;
}
// getDepth()函数也可以简写为一行
// int getDepth(int x) { return d[x] = (d[x] == -1) ? dfs(fa[x]) + 1 : d[x]; }
int main() {
    scanf("%d%d", &n, &m);
    memset(d + 2, -1, sizeof(int) * (n - 1));   // 使用数组d要记得把2 ~ N的所有元素初始化为-1
    while (m--) {                               // 因为d[1] = 0, 所以无需改变d[1]的值
        int k, x, y;
        scanf("%d%d", &x, &k);
        hasChild[x] = true;     // 结点x有孩子, 因此它不可能是叶子结点
        while (k--) {
            scanf("%d", &y);
            fa[y] = x;          // 结点y的父亲结点为x
        }
    }
    // 注意i需要由1开始, 这是因为这棵树可能只有1个结点, 这时结点1不仅为根结点也为叶子结点
    for (int i = 1; i <= n; ++i)
        if (!hasChild[i]) {
            ++cnt[getDepth(i)];                  // 结点i为叶子结点, 它的高度上的叶子结点个数加一
            height = max(getDepth(i), height);   // 更新树的高度
        }
    for (int i = 0; i <= height; ++i)       // 输出结果
        printf("%d%s", cnt[i], i != height ? " " : "\n");
    return 0;
}

方法二、邻接表

使用邻接表存储树,判断结点x是否为叶子结点只需判断结点x是否有孩子即可。遍历可使用dfs或者bfs

时间复杂度与空间复杂度都为: O ( N ) O(N) O(N)

(有点巧合的是这段代码行数跟上段代码一样QwQ)

#include 
using namespace std;
const int MAXN = 100;
vector<int> G[MAXN];
int n, m, cnt[MAXN];
// x是这次递归的结点, depth是结点x的深度, 返回最大深度
int dfs(int x, int depth) {
    int maxDepth = depth;
    if (G[x].empty()) ++cnt[depth]; // 如果x是叶子结点, 深度为depth的叶子结点数量加1
    else for (auto t : G[x])        // 否则遍历结点x的孩子
        maxDepth = max(maxDepth, dfs(t, depth + 1));
    return maxDepth;
}
int main() {
    scanf("%d%d", &n, &m);
    while (m--) {
        int k, x, y;
        scanf("%d%d", &x, &k);
        while (k--) {
            scanf("%d", &y);
            G[x].push_back(y);
        }
    }
    int height = dfs(1, 0);         // 返回的结果就是树的高度
    for (int i = 0; i <= height; ++i)
        printf("%d%s", cnt[i], i != height ? " " : "\n");
    return 0;
}

你可能感兴趣的:(PAT甲级)