蓝桥杯2021年真题演练——7、 左hai子右兄弟(JavaA组)

题目描述

在这里插入图片描述
在这里插入图片描述

题目解析

⭐⭐首先要知道什么是“左孩子右兄弟”表示法,不然这个题是没法做的。
🌙通常来说,通过“左孩子右兄弟表示法”得到的二叉树树应该是唯一确定的,但如果规定孩子节点的顺序是无序的,那结果就千变万化了,比如测试样例中孩子节点"2",“3”,"4"的顺序不同也就生成了三种不同的二叉树,其中第三颗树的高度最大。题目即要求任意输入一棵树,得到它所能生成的高度最大的二叉树的高度。
这里要注意,题目中规定根节点的高度为0,不同于力扣,力扣中通常为1。

解题思路

⭐⭐根节点的孩子节点在转换为二叉树后一定不在同一层了,即"右兄弟",这些孩子节点每一个都贡献了高度。
如下图中,根节点的孩子节点2,3,4均贡献了一个高度。
在这里插入图片描述

但是发现节点5并没有贡献高度,要让它贡献高度必须将其放到最右边,如图:
在这里插入图片描述
🌙再复杂一些的图也一样,根节点的每个孩子节点都贡献了一个高度,而要得到高度最大的二叉树则需要把孩子节点中,高度最大的节点(这个高度是指转化为二叉树之后),放到最右边(其实也是最下边,这样才能贡献更可能多的高度)。而孩子节点的最大高度也通过相同的方法递归求得就好了。不会写递归的话可以看一下这篇:你还在靠玄学写递归吗?
如果用数组dp来存储得到的结果那么dp[1]=max(dp[2],dp[3],dp[4])+3(针对测评样例),最后只要输出dp[1]即可
🌙其实还有一个关键是,用什么方法存储一颗树,我刚开始用的是力扣题中常用的方法,如下:

class TreeNode{
int val;
List<TreeNode> children;//存储孩子节点
TreeNode(){}
}

⭐但我用这种存储方式发现它无法通过评测系统,耗时太长啦!超过1秒就直接pass掉了,但也能通过部分评测用例,能得一些分。
⭐我们还可以用邻接表来存储树,其中邻接表通常用数组来实现,亲测可以通过。
构造一个数组Edge[],来存储各边的信息,当然,邻接表还需要一个数组head[]来记录起点位置,采用头插法插入一条边。
Edge

class Edge{
int  next;   //指向下一个孩子节点的位置(如果为0说明没有下一个了)
int  to;     //孩子节点的值
}

头插法:

public static void addEdge(int farther,int son) {
		//头插法
		tot++;
		edge[tot].to=son;//建立边:farther->son
		edge[tot].next=head[farther];//新添加的边指向原来的头
		head[farther]=tot;//新边作为新的头
	}

再举个例子理解一下:
对于题目中的样例,首先插入边1->2(注意,这里不使用下标0,直接从1开始)

12345
head00000
edge.next00000
edge.to20000

插入边1->3

12345
head20000
edge.next01000
edge.to23000

插入边1->4

12345
head30000
edge.next01200
edge.to23400

插入边2->5

12345
head34000
edge.next01200
edge.to23450

比如查看一下节点2的孩子节点,首先从head中找到起点,即head[2]=4,然后在edge中寻找,edge[4].to=5,说明有一条2->5的边,然后发现edge[4].next=0,即没有别的孩子了。

代码实现

import java.util.*;
public class Main {
	public static  Edge[] edge;//存储边的信息
	public static int[] head;//存储各节点在edge中的起点
	public static int[] count;//计数各节点的孩子节点的个数
	public static int tot;//addEdge用到的一个变量
	public static int[] dp;
	public static void main(String[] args) {
		//采用邻接表存储树(数组统一以1为起点)
		Scanner scan=new Scanner(System.in);
		int n=scan.nextInt();//节点个数(边个数为n-1)
		edge=new Edge[n+1];
		for(int i=1;i<=n;i++) edge[i]=new Edge();
		head=new int[n+1];
		count=new int[n+1];
		dp=new int[n+1];
		//录入边的信息
		for(int son=2;son<=n;son++) {
			int farther=scan.nextInt();
			addEdge(farther,son);
			count[farther]++;
		}
		getMax(1);
		System.out.println(dp[1]);
		scan.close();
	}
	public static void getMax(int u) {
		int max=0;
		for(int i=head[u];i!=0;i=edge[i].next) {
			int v=edge[i].to;
			getMax(v);
			max=Math.max(max, dp[v]);
		}
		dp[u]=count[u]+max;
		return ;
	}
	public static void addEdge(int farther,int son) {
		//头插法
		tot++;
		edge[tot].to=son;//建立边:farther->son
		edge[tot].next=head[farther];//新添加的边指向原来的头
		head[farther]=tot;//新边作为新的头
	}
}
class Edge{
	int next;//存储下一个孩子节点的下标
	int to;//存储孩子节点
}




在这里插入图片描述

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值