Tree Reconstruction UVA - 10410 (根据树的BFS和DFS建树)

解题思路:

题目给出了一棵树的层序遍历和先序遍历

我们取依次取先序遍历的一个结点a和这个结点的下一个结点b。

然后分类讨论:

1.如果a为根节点,那么b必定为a的子节点,那么我们将b的父节点设为a,

更新a的子结点序列

2.如果b在层序遍历中的位置正好在a的后面一个,那么我们可以断定b为a的子结点或者为a的兄弟结点,由于题目要求层序遍历是从小到大排的,那么

通过比较a,b的大小可以确定关系

如果b大于a,那么b是a的兄弟结点

如果b小于a,那么b是a的子结点

3.如果b在层序遍历中的位置在a的后面,那么b肯定是a的子结点

4.如果b在层序遍历中的位置在a的前面,那么b可能为a的祖先结点的子结点

我们在二叉树中回溯a结点的父节点,回溯到a的祖先结点在层序遍历中的位置在b前面时,此时a的祖先结点为b的祖先结点,这时,我们再根据2中的步骤判断即可。

本题用到了回溯思想,一个结点的父节点可以用前驱数组保存,结点在层序遍历中的位置也可以用数组保存。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stack>
#include <algorithm>
#include <string.h>
#include <vector>
#include <queue>
#include <stdio.h>
#include <stack>
using namespace std;
const int MAXN = 10010;
int N;
int layerOrders[MAXN];
int preOrders[MAXN];
vector<int> subNodes[MAXN];
//位置索引
int posIndexes[MAXN];
int sfFather[MAXN];  //父节点前驱
void initSet() {
	for (int i = 1; i <= N; ++i) {
		sfFather[i] = i;   //
	}
}
int main() {
	while (scanf("%d", &N) != EOF) {
		memset(subNodes, 0, sizeof(subNodes));
		initSet();  //初始化
		for (int i = 0; i < N; ++i) {
			scanf("%d", &layerOrders[i]);   //层序便利
			posIndexes[layerOrders[i]] = i;  //更新位置索引用来判断
		}
		for (int i = 0; i < N; ++i) {
			scanf("%d", &preOrders[i]);     //先序遍历
		}
		//根据先序判断
		for (int j = 0; j < N - 1; ++j) {
			int u = preOrders[j];
			int v = preOrders[j + 1];
			if (j == 0) {  //特殊:根节点的情况:下一个结点必为子节点
				subNodes[u].push_back(v);
				sfFather[v] = u;  //更新父节点
				continue;
			}
			//层序判断
			//第一种情况:层序中u+1 == v,判断大小
			if (posIndexes[u] + 1 == posIndexes[v]) {
				//如果v的值大于u,那么就是兄弟结点
				if (v > u) {
					subNodes[sfFather[u]].push_back(v);
					sfFather[v] = sfFather[u];  
				}
				//如果v的值小于u,那么就是子节点
				else {
					subNodes[u].push_back(v);
					sfFather[v] = u;
				}
			}
			//第二种情况:v在u的较后面:子节点
			else if (posIndexes[v] - posIndexes[u] > 0) {
				subNodes[u].push_back(v);
				sfFather[v] = u;
			}
			else if (posIndexes[v] - posIndexes[u] < 0) {   //第三种情况:v在u的前面:回溯
				int ti = j - 1;
				int curNode = preOrders[ti];
				while (posIndexes[v] - posIndexes[curNode] < 0) {
					ti--;
					curNode = preOrders[ti];  //往前回溯
				}
				//回溯完毕,调用1中的算法
				if (posIndexes[curNode] + 1 == posIndexes[v]) {
					if (v > curNode) {
						subNodes[sfFather[curNode]].push_back(v);  
						sfFather[v] = sfFather[curNode];
					}
					else {
						subNodes[curNode].push_back(v);
						sfFather[v] = curNode;
					}
				}
				else if (posIndexes[v] - posIndexes[curNode] > 0) {
					subNodes[curNode].push_back(v);
					sfFather[v] = curNode;
				}
			}
		}
		for (int i = 1; i <= N; ++i) {
			printf("%d:", i);
			for (int j = 0; j < subNodes[i].size(); ++j) {
				printf(" %d", subNodes[i][j]);
			}
			printf("\n");
		}
	}
	system("PAUSE");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值