[刷题之旅no21]P3884 [JLOI2009]二叉树问题

题目
输入:二叉树结点数量
结点之间相邻情况
需要求距离的两个结点编号
输出:二叉树的深度,宽度和给定两个编号之间的距离
问题所在:
1.选定一个比较好的数据结构储存题目给定的数据
2.利用这个数据结构把树的深度,宽度和给定两个结点之间的距离求出来。

一、求树的深度:
从根节点遍历整个树
记录当前深度,时刻更新最大值即可

二、求树的宽度:
设置层级概念,也就是当前节点到根节点的路程长度+1;
统计所有层级的节点数,取最大值即可。

三、给定两个结点之间的距离:
采用由下向上的方法
例如给定两个节点a,b.那么从a,b开始一直向上搜索到相遇点c
记录a到c的路程,b到c的路程。然后距离l=lac2+lbc1;
关键是如何判断某个根节点是两个节点的共同父节点呢?
方法:可以先让往上走,每到一个节点,就在数组中对应下标(下标等于路过节点的编号)记录a节点到此节点的路程;然后从b开始向上遍历,如果遇到的节点编号在数组中值不是0,那么利用当前b的路程长度和数组值进行计算再break即可。
对,还要时刻判定当前a节点是否走过了b,或者b走过了a,两个都判断一下,来确定谁在上方,谁在下方。

好,现在根据上面的思想来确定一个好的数据结构。
输出方面没有顺序可言,所以不可以一步一步构筑我们所需要的树
链式前向星?不太好,我们并不是很关心兄弟节点的情况。
对于一个确定的节点,我只需要直到他的父节点是谁,和他的子节点是谁。
好吧,我终于知道数据结构的重要意义了,对于一个二叉树问题,好的数据结构选取对于解题无疑是至关重要的。
关键是如何储存一个二叉树呢?
ok,差不多思路算是有了!
数据结构就选链式前向星
1.读取
2.深搜,并且在深搜的过程中记录所有节点的父节点
3.用循环,执行上面的思路即可
中间遇到的问题:
关于循环寻找路径上的逻辑问题
三种情况:
1.起点在终点下面
2.起点在终点上面
上面两种情况还有st或者end是否为1的情况
3.起点和终点没有直系关系
这个最好解决
向大佬学习:
树剖,树链剖分LCA,看来还是给自己省一点时间吧。哈哈,先记上,写不出来题的时候再学
放代码:

#include<stdio.h>
int lst[105]={0},lend[105]={0},father[105]={0},width[105]={0},head[105]={0};
typedef struct
{
	int to,next;
}Edge;
Edge edge[105];
int max_depth=0,max_width=0,cnt=0,n=0,st=0,end=0,from=0,to=0,length=0;
void createdge(int from,int to);
void dfs(int s,int f,int sGrade);
int main()
{
	//读取
	scanf("%d",&n);
	for(int i=0;i<n-1;i++)
	{
		scanf("%d %d",&from,&to);
		createdge(from,to);
		createdge(to,from);
	}
	scanf("%d %d",&st,&end);
	dfs(1,0,1);
	printf("%d\n",max_depth);
	printf("%d\n",max_width); 
	//注意这个循环容易出现的逻辑问题
	//我们的目的就是要避免错误逻辑现象的发生
	//出错情况,就是st=1时我们的判断。应该放在整个循环的首部,因为如果没判断直接让st向上跨越,那么会出现st=0的情况,那么整个lst数组都没有值,在end寻找st的时候也找不到它了。所以判断st==1的位置应该放在整个循环的首位。
	while(st!=1)
	{
		st=father[st];//向上跨越 
		length++;
		lst[st]=length;
		if(st==end)
		{
			printf("%d\n",lst[st]*2);
			return 0;
		}
	}
	length=0;
	while(1)//两种情况结束,一种我向上走遇到了st,另一种公共节点不为0 
	{
		end=father[end];
		length++;
		lend[end]=length;
		if(lst[end]!=0)
		{
			printf("%d\n",lst[end]*2+lend[end]);
			return 0;
		}
		if(end==st)
		{
			printf("%d\n",lend[end]);
			return 0;
		}
	}
}

void createdge(int from,int to)
{
	cnt++;
	edge[cnt].next=head[from];
	edge[cnt].to=to;
	head[from]=cnt;
}

void dfs(int s,int f,int sGrade)
{
	//记录父节点
	father[s]=f; 
	for(int i=head[s];i;i=edge[i].next)
	{
		if(edge[i].to!=f)//当前节点不是父节点 
		{
			if(sGrade+1>max_depth)
			{
				max_depth=sGrade+1;
			}
			width[sGrade+1]++;
			if(width[sGrade+1]>max_width)
			{
				max_width=width[sGrade+1];
			}
			dfs(edge[i].to,s,sGrade+1);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值