UVA - 10410 Tree Reconstruction(根据dfs和bfs还原树)

本文深入探讨了如何利用广度优先搜索(BFS)和深度优先搜索(DFS)序列构建一棵树的结构,详细解析了易错点与常见误解,并通过具体算法实现了这一过程。文章分享了从困惑到解决的全过程,包括对题目的逐步理解和算法的实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此题感慨良多。

首先,我看到这个题我是觉得题意简单好懂,但是恰恰最开始就把我搞糊涂了。

输入:第一行 节点数n、第二行 bfs序列,第三行 dfs序列。

输出:输出最初看不懂,原题是真的读不懂= =|||。看了好几遍书上的题目,才注意到输出的是编号n的子节点,例如 编号4有子节点3、5 那么 4: 3 5。

 

题意还容易误解,在此强调,以免后来者和我犯一样的错误。

误解1:这棵树是一个二叉树(从来没说过,不要先入为主)。

误解2:树的节点是先访问先输出(bfs和dfs序和我们要输出的答案都是升序的)(不要漏看题意)。

 

我刚开始想的是和先序和中序一样,一个个确定根节点递归实现,后来不管怎么想都觉得难以实现。

最后确实是苦读博客(感谢大佬QAQ)才勉强写完。

 

我们可以发现以下几件事情

1.bfs序列中相邻的u和v可能是兄弟节点,dfs序列中相邻的u和v可能是父子节点。

2.因为一个节点按升序展开,所以兄弟节点u和v,u<v,在bfs节点中,就会有u和v相邻,且输入是v紧跟u,以及u<v。

3.我们现在知道了2这件事情,那么如果bfs序列中有一个v紧跟u,但是u>v,这意味着什么?这意味着v不是u的兄弟节点,所以v是一个子节点,是u的第一个兄弟节点的子节点。

4.dfs序列中,相邻的u和v可能是父子节点,所以如果有u和v满足3而且在dfs序列中相邻,那么v是u的子节点。

5.如果两个节点u和v是兄弟节点那么在bfs序中一定是相邻的。

 

了解了以上情况,我们可以的到可以判断子节点的几个条件

1.bfs序中节点u,紧跟节点u的v,u>v

2.dfs序中节点u,紧跟节点u的v,在bfs序中不相邻

 

所以现在我们的问题变成了如何边确认dfs序列相邻又要确认在bfs中是否相邻。暴力点,每次判断都遍历一遍,n最大为1000好像是可以……但是也麻烦。

我看的大佬的方法是借助栈,用栈保存dfs序列,然后用一个数组保存bfs的距离,即u和v相邻,且v紧跟u,那么pos[u]=pos[v]+1。

不过为什么用栈呢,我也不懂,我猜是因为dfs是栈实现的(瞎猜的没有任何依据的)。

 

那么,现在我们就可以挨个遍历dfs,上一个节点是stack的top,也就是u,现在遍历到的点是t,u和t在dfs序列中相邻,且t到u后边。

所以以下条件就可以直接判断为u是t的父节点

1.pos[u]+1==pos[t] && u>t

2.pos[u]+1<pos[t]

3.root==u

我的理解也就到此为止了,感觉还有很多细节没有很明白,以后再看吧……

#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
using namespace std;

const int maxn = 1005;

int pos[maxn];
vector<int> G[maxn];
stack<int>s;
int main()
{
	int n;
	while (cin >> n)
	{
		int t;
		for (int i = 1; i <= n; i++)
		{
			scanf("%d", &t);
			pos[t] = i;
			G[i].clear();
		}
		int root;
		scanf("%d", &root);
		s.push(root);
		for (int i = 1; i < n; i++)
		{
			scanf("%d", &t);
			for (;;)
			{
				int u = s.top();
				if ((pos[u] + 1 == pos[t] && u > t) || pos[u] + 1 < pos[t] || u == root)
				{
					G[u].push_back(t);
					s.push(t);
					break;
				}
				else
					s.pop();
			}
		}
		for (int i = 1; i <= n; i++)
		{
			printf("%d:",i);
			for (int j = 0; j < G[i].size(); j++)
			{
				printf(" %d", G[i][j]);
			}
			cout << endl;
		}
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值