⭐⭐首先要知道什么是“左孩子右兄弟”表示法,不然这个题是没法做的。
通常来说,通过“左孩子右兄弟表示法”得到的二叉树树应该是唯一确定的,但如果规定孩子节点的顺序是无序的,那结果就千变万化了,比如测试样例中孩子节点"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开始)
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
head | 0 | 0 | 0 | 0 | 0 |
edge.next | 0 | 0 | 0 | 0 | 0 |
edge.to | 2 | 0 | 0 | 0 | 0 |
插入边1->3
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
head | 2 | 0 | 0 | 0 | 0 |
edge.next | 0 | 1 | 0 | 0 | 0 |
edge.to | 2 | 3 | 0 | 0 | 0 |
插入边1->4
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
head | 3 | 0 | 0 | 0 | 0 |
edge.next | 0 | 1 | 2 | 0 | 0 |
edge.to | 2 | 3 | 4 | 0 | 0 |
插入边2->5
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
head | 3 | 4 | 0 | 0 | 0 |
edge.next | 0 | 1 | 2 | 0 | 0 |
edge.to | 2 | 3 | 4 | 5 | 0 |
比如查看一下节点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;//存储孩子节点
}