题目描述
大致意思就是:
给你一棵树(不是二叉树,就是普通的树),输入数据会告诉你有多少个非叶子节点,以及这些非叶子节点各自的子节点列表,让你构建出这个树,最终从上到下依次输出每一层的叶子节点个数。
我的思路:
利用深度优先的想法来处理。
先用一种数据结构把非叶子节点和它的子节点列表存在一起,比如C++的vector容器,JAVA的Map集合,都可以实现类似的效果。这样能有效的帮助我们构造出这棵树的整体样貌。
然后说一下怎么处理。
· 我们需要一个数组来记录每一层的叶子节点数,题目也说了,总节点数0<N<100,那么这棵树最多是100层的,所以我们定义一个leaf[100],leaf[i] 就是i层的叶子节点数。
· 在每一次递归中,我们要知道自己当前是哪个节点,在哪一层,所以dfs可以定义成:
void dfs(int cur , int level)
然后,学过数据结构的同学肯定不难想到,逐层向下递归的过程中,到了叶子节点那肯定就不能再往下了,所以说判断当前节点是不是叶子节点,就是我们的退出条件。然后,除了退出,我们还要把当前层对应的叶子节点数+1,即 leaf[level] ++
如果当前节点不是叶子节点,那我们就从vector容器中拿出当前节点的子节点列表,然后挨个传进去调用dfs进行递归。等到dfs执行结束,我们的leaf数组也就更新完毕了,接下来从0层开始一直到最大深度,逐层输出leaf [ i ]即可。
代码实现
我一开始因为对C++了解的不够多,不认识vector这种东西,出于懒得写链表,就用JAVA写了一个版本(List很好用捏),后来对vector有了部分了解后,用C++重写了一版。
JAVA实现:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
public class Problem1004 {
// 因为Max(N) = 100 , 所以最大层数就是100 ,开个100大小的数组来记录每层的叶子节点数
private static int[] nums = new int[100];
private static int maxLevel = 0;
// 用Map来存放非叶子节点以及它的子节点列表
private static Map<Integer, List<Integer>> all = new HashMap<>();
public static void dfs(int cur,int level) {
if(level>maxLevel) {
// 更新最大层数
maxLevel = level;
}
// map里没存当前节点,说明当前节点就是个叶子节点,对应层叶子数+1并退出
if(!all.containsKey(cur)) {
//没有子节点,当前节点是个叶子节点,对应层数的叶子节点数+1
nums[level]++;
return;
}
// 接下来,若不是叶子节点则继续向下走
for(Integer son:all.get(cur)) {
dfs(son, level+1);
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int N,M;
N = scanner.nextInt();
M = scanner.nextInt();
for(int i=0;i<M;i++) {
// 先输入这个非叶子节点的信息,作为根节点
Integer rootOrder = scanner.nextInt();
// 用来暂时存储这非叶子节点的所有子节点
List<Integer> tempList = new ArrayList<>();
// 这一层的节点数
int k = scanner.nextInt();
for(int j=0;j<k;j++) {
// 把儿子节点存入临时集合
Integer order = scanner.nextInt();
tempList.add(order);
}
// 将根节点存入总记录表中
all.put(rootOrder, tempList);
}
dfs(1,0);
System.out.print(nums[0]);
for(int i=1;i<=maxLevel;i++) {
System.out.print(" " + nums[i]);
}
System.out.println();
}
}
C++实现:
#include<iostream>
#include<vector>
using namespace std;
/*
已AC 2022/11/28 晚
JAVA交了一版,AC了,这次试着用c++来重新写一下
*/
// 第一次尝试vector,这里用来对应每个根节点的子节点列表(即sons[i] 就是i节点的子节点列表)
vector<int> sons[100];
// leaf数组用来记录每一层的叶子节点的个数
int leaf[100];
int maxLevel = 0;
void dfs(int cur,int level){
// 若当前深度(层数)已经大于之前记录的最大深度,那么就更新最大层数的值
if(level>maxLevel){
maxLevel = level;
}
if(sons[cur].size()==0){
// 说明当前节点没有子节点,是个叶子节点,那么这一层的叶子节点数应该+1
leaf[level]++;
// 因为没有子节点,所以退出递归
return;
}
//接下来处理有子节点的
for(int i=0;i<sons[cur].size();i++){
dfs(sons[cur][i],level+1);
}
}
int main(){
int N,M,temp,ID,K;
cin>>N>>M;
for(int i=0;i<M;i++){
cin>>ID>>K;
for(int j=0;j<K;j++){
cin>>temp;
// 将子节点的序号加入到根节点的子节点列表中去
sons[ID].push_back(temp);
}
}
// 题目规定01是第一个根节点,我们用int接收,所以这里传1,同时它是第0层的
dfs(1,0);
// 递归计算处理后,leaf数组已经被更新,我们只需要从0到最大层数进行输出即可(因为再往后面的层压根也没有节点)
//啊对,这里因为他要求最后一个结果后面不能有空格,所以我们先输出一个 然后按照 空格 值的格式输出
cout<<leaf[0];
for(int j=1;j<=maxLevel;j++){
cout<<" "<<leaf[j];
}
cout<<endl;
return 0;
}
二者效率比较:
(JAVA实现)
可能是我List、Map占的空间比较多了,不过还是过了,没有超出限制。
(C++实现)
思路差不多,C++的效果会好一些,也有可能我JAVA写太烂- -