二叉树专题--洛谷 P3884 [JLOI2009]二叉树问题(dfs求二叉树深度 bfs求二叉树宽度 dijkstra求最短路)

33 篇文章 1 订阅
12 篇文章 0 订阅

[JLOI2009]二叉树问题

题目描述

如下图所示的一棵二叉树的深度、宽度及结点间距离分别为:

  • 深度: 4 4 4
  • 宽度: 4 4 4
  • 结点 8 和 6 之间的距离: 8 8 8
  • 结点 7 和 6 之间的距离: 3 3 3

其中宽度表示二叉树上同一层最多的结点个数。

给定一颗以 1 号结点为根的二叉树,请求出其深度、宽度和两个指定节点 x , y x, y x,y 之间的距离。

输入格式

第一行是一个整数,表示树的结点个数 n n n
接下来 n − 1 n - 1 n1 行,每行两个整数 u , v u, v u,v,表示树上存在一条连接 u , v u, v u,v 的边。
最后一行有两个整数 x , y x, y x,y,表示求 x , y x, y x,y 之间的距离。

输出格式

输入三行,每行一个整数,依次表示二叉树的深度、宽度和 x , y x, y x,y 之间的距离。

样例 #1

样例输入 #1

10                                
1 2                            
1 3                            
2 4
2 5
3 6
3 7
5 8
5 9
6 10
8 6

样例输出 #1

4
4
8

提示

对于全部的测试点,保证 1 ≤ u , v , x , y ≤ n ≤ 100 1 \leq u, v, x, y \leq n \leq 100 1u,v,x,yn100,且给出的是一棵树。

题意:

给定一颗 1 号结点为根的二叉树,请求出其 深度、宽度和两个指定节点 x, y 之间的距离

思路:

二叉树深度比较好求,直接 dfs 求解 即可。

对于 二叉树的宽度,在这里提供一个不错的思路:

在宽搜的过程中,我们可以 在队列中添加一个 “0” 标记,作为 宽搜时扩展的层与层之间的区分,同时也是 该层结束的标志(一层的结尾)

我们举个例子,假设有这样 一棵二叉树
在这里插入图片描述
最开始我们在 第一层 根节点后 添加一个 “0” 标记(第一层只有根),对于 第二层,当遍历到 第一层的 “0” 时,直接在 第二层 结尾处 扩展出一个 “0” 标记 即可。

同样的道理,当遍历到第二层的 “0” 时,直接在第三层 结尾处 扩展出一个 “0” 标记

每次取得队头 时,如果 对头值为 0,那么我们 判断一下当前层节点的个数,并 更新最大值 ans

最终 ans 即为二叉树的宽度

代码片段如下:

int bfs(int u) //宽度 返回值即为二叉树宽度
{
	int ans = 0;
	vector<int> level;		//用一个辅助的向量 level 来存储某一层的所有节点,其大小作为更新二叉树宽度 ans 的依据
	queue<int> q; q.push(u), q.push(0);	//最开始将根节点和 “0” 标记 压入队列
	while (q.size())
	{
		auto t = q.front(); q.pop();
		if (!t)		//如果 t 是 “0” 标记,说明到了某一层的尽头
		{
			if (level.empty()) break;	//如果当前层结点数为 0,说明二叉树所有节点已经遍历完毕,直接结束遍历
			ans = max(ans, (int)level.size());		//否则更新答案(二叉树宽度)
			q.push(0);	//在下一层结尾处扩展一个 “0” 标记
			level.clear();	//清空当前层,准备存下一层
			continue;
		}
		level.push_back(t);		//先将当前点加入
		if (l[t]) q.push(l[t]);	//加左儿子
		if (r[t]) q.push(r[t]);	//加右儿子
	}
	return ans;
}

对于给定树中两节点求距离,我们这里直接先用 邻接表存图,之后用 朴素 dijkstra 求解最短路 即可。

注意:

  • 在本题中,从二叉树 上方节点往下走每条边权值为 1,而从下往上走每条边权值为 2,好像题面没有提及。

代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <bits/stdc++.h>

using namespace std;
//#define int long long
//#define map unordered_map
const int N = 110, M = (N << 1), inf = 0x3f3f3f3f;
map<int, int> l, r;
int n;
int depth = -1;
int g[N][N];
int dist[N];
bool st[N];

void dfs1(int u, int dep)//深度
{
	if (!l[u] && !r[u])
	{
		depth = max(depth, dep);
		return;
	}
	if (l[u]) dfs1(l[u], dep + 1);
	if (r[u]) dfs1(r[u], dep + 1);
}

int bfs(int u) //宽度 由于本题并没有要求输出某层的节点,因此我们直接同一个 int width 记录当前层节点个数即可,并用 width 更新 ans
{
	int ans = 0;
	int width = 0;	//此处就不用 vector 了,嫌麻烦
	queue<int> q; q.push(u), q.push(0);
	while (q.size())
	{
		auto t = q.front(); q.pop();
		if (!t)
		{
			if (!width) break;
			ans = max(ans, width);
			q.push(0);
			width = 0;
			continue;
		}
		width++;
		if (l[t]) q.push(l[t]);
		if (r[t]) q.push(r[t]);
	}
	return ans;
}

int dijk(int a, int b)
{
	memset(dist, 0x3f, sizeof dist);
	memset(st, false, sizeof st);
	dist[a] = 0;
	for (int i = 0; i < n; ++i)
	{
		int t = -1;
		for (int j = 1; j <= n; ++j)
		{
			if (!st[j] && (t == -1 || dist[t] > dist[j])) t = j;
		}
		st[t] = true;
		for (int j = 1; j <= n; ++j)
		{
			dist[j] = min(dist[j], dist[t] + g[t][j]);
		}
	}
	return dist[b];
}

signed main()
{
	int T = 1; //cin >> T;

	while (T--)
	{
		memset(g, 0x3f, sizeof g);
		cin >> n;
		for (int i = 0; i < n - 1; ++i)
		{
			int x, y; cin >> x >> y;
			if (!l[x]) l[x] = y;
			else if (!r[x]) r[x] = y;
			g[x][y] = min(g[x][y], 1);
			g[y][x] = min(g[y][x], 2);
		}
		dfs1(1, 1);
		cout << depth << '\n';
		cout << bfs(1) << '\n';
		int a, b; cin >> a >> b;
		cout << dijk(a, b) << '\n';
	}

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值