P1827 [USACO3.4] 美国血统 American Heritage(前序 + 中序 生成后序)

2023-09-22 11:20:28

P1827 [USACO3.4] 美国血统 American Heritage(前序 + 中序 生成后序)

一、前言

二叉树入门题。涉及到树的基本知识、树的结构、树的生成。
本文从会从结构,到完成到,优化。

二、基础知识

Ⅰ、二叉树的遍历

  • 前序遍历: 左右
  • 中序遍历:
  • 后序遍历: 左右

通过上面的观察,可得根在那,就是什么方式的遍历

Ⅱ、二叉树的结构

二叉树的结构:节点值 + 左节点指针 + 右节点指针

// c++的结构体写法
struct node {
	char val;
	node *left;
	node *right;
	node() : val(0), left(nullptr), right(nullptr){}
    node(int val) : val(val), left(nullptr), right(nullptr){}
    node(int val, node *left, node *right) : val(val), left(left), right(right){}
};
// c语言结构体写法
struct node {
	char val;
	struct node *left;
	struct node *right;
	node() : val(0), left(NULL), right(NULL){}
    node(int val){
    	val = val;
    	left = NULL;
    	right = NULL;
    {
    node(int val, struct node *left, struct node *right) {
		val = val;
    	left = left;
    	right = right;
	}
};

三、直接思路

通过 前序 + 中序 直接生成 树。然后再前序遍历(可以过)
现在的问题,就变成了。怎么生成树了。
估计大家在学习数据结构,二叉树这一章节中。老师肯定讲过手写这个题(通过前序或后序找到根节点,然后把中序分成两部分,左子树,右子树)。但是现在怎么把他变成代码呢?

#include <iostream>
using namespace std;

struct node {
	char val;
	node *left;
	node *right;
	node() : val(0), left(nullptr), right(nullptr){}
	node(char x) : val(x), left(nullptr), right(nullptr){}
	node(char x, node *left, node *right) : val(x), left(left), right(right){}
};
/*
s1 中序
[inorderBegin, inorderEnd)
s2 前序
[preorderBegin, preorderEnd)
上述就是现在树的范围
再分割子树的范围就可以了
明白范围!!!左端点可取,右端点不可取
*/
node *traversal(string s1, int inorderBegin, int inorderEnd, string s2, int preorderBegin, int preorderEnd) {
	if (preorderEnd == preorderBegin) return nullptr;
	char val = s2[preorderBegin];
	node *root = new node(val);

	if (preorderEnd - preorderBegin == 1) return root;

	int delimiterIndex; // 左右子树的分割点
	for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
		if (s1[delimiterIndex] == val) break;
	}
	
	// 左子树
	// 中序
	int leftInorderBegin = inorderBegin;
	int leftInorderEnd = delimiterIndex;
	// 前序
	int leftPreorderBegin = preorderBegin + 1;
	int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin;

	// 右子树
	int rightInorderBegin = delimiterIndex + 1;
	int rightInorderEnd = inorderEnd;
	int rightPreorderBegin = preorderBegin + delimiterIndex - inorderBegin + 1;
	int rightPreorderEnd = preorderEnd;

	root->left = traversal(s1, leftInorderBegin, leftInorderEnd, s2, leftPreorderBegin, leftPreorderEnd);
	root->right = traversal(s1, rightInorderBegin, rightInorderEnd, s2, rightPreorderBegin, rightPreorderEnd);
	return root;
}

void dfs(node *root) {
	if (!root) return ;
	dfs(root->left);
	dfs(root->right);
	cout << root->val;
}


int main() {
	node *tree;
	string s1, s2;
	cin >> s1 >> s2;
	tree = traversal(s1, 0, s1.size(), s2, 0, s2.size());
	dfs(tree);
	return 0;
}

在这里插入图片描述

四、优化

通过上面可以发现,他在生成树的过程中,就是经行的后续遍历。所以不用直接生成树。

#include <iostream>
using namespace std;

void traversal(string s1, int inorderBegin, int inorderEnd, string s2, int preorderBegin, int preorderEnd) {
	if (preorderEnd == preorderBegin) return;
	char val = s2[preorderBegin];

	int delimiterIndex; // 左右子树的分割点
	for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
		if (s1[delimiterIndex] == val) break;
	}
	
	// 左子树
	// 中序
	int leftInorderBegin = inorderBegin;
	int leftInorderEnd = delimiterIndex;
	// 前序
	int leftPreorderBegin = preorderBegin + 1;
	int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin;

	// 右子树
	int rightInorderBegin = delimiterIndex + 1;
	int rightInorderEnd = inorderEnd;
	int rightPreorderBegin = preorderBegin + delimiterIndex - inorderBegin + 1;
	int rightPreorderEnd = preorderEnd;

	traversal(s1, leftInorderBegin, leftInorderEnd, s2, leftPreorderBegin, leftPreorderEnd);
	traversal(s1, rightInorderBegin, rightInorderEnd, s2, rightPreorderBegin, rightPreorderEnd);
	cout << val;
}

int main() {
	string s1, s2;
	cin >> s1 >> s2;
	traversal(s1, 0, s1.size(), s2, 0, s2.size());
	return 0;
}

五、相关题目

六、最后

创作不易,留个赞,鼓励一下把!

更多推荐

SLAM从入门到精通(消息传递)

【声明:版权所有,欢迎转载,请勿用于商业用途。联系信箱:feixiaoxing@163.com】前面我们只是编写了一个publisher节点,以及一个subscribe节点。有了这两个节点,它们之间就可以通信了。在实际生产中,我们除了简单的通信之外,要传递的数据可能还有很多。这个时候,我们就要构建一个消息体。这个消息体

C# 流Stream详解(3)——FileStream源码

【FileStream】构造函数如果创建一个FileStream,常见的参数例如路径Path、操作方式FileMode、权限FileAccess。这里说下FileShare和SafeFileHandle。我们知道在读取文件时,通常会有两个诉求:一是如何更快的读取文件内容;二是如何减少读取文件的消耗。常见的加快读取文件的

在ubuntu20.04中创建虚拟机:Oracle VirtualBox - 7中安装Windows-10(64bit)

问题描述之前一直在用ubuntu20.04,但是今天遇到一个需求:需要判定.exe文件是否可以正常运行,这样一来可能就需要一个虚拟机来佐助了,当然也搜了一些其他的处理办法,但是我大概看了一下,并不能满足我的需求。如果有需要请看:linux下如何完美运行exe文件?https://www.zhihu.com/questi

C#实现异步方式

在异步程序中,程序代码不需要严格按照编写时的顺序执行为了改善代码性能,有时候需要在一个新的线程中运行一部分代码有时候无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序也就是说:异步编程赋予代码非顺序执行的能力,让程序能够在部分耗时操作的同时,干其他的事情一、通过委托实现异步如果委托对象在调用列表中

【LeetCode-困难题】239. 滑动窗口最大值

文章目录题目方法一:单调双端队列题目方法一:单调双端队列if(deque.peekFirst()==nums[i-k])deque.removeFirst();这一步很关键,当队首元素(最大元素)是滑动窗口后要被抛弃的元素时,他就不能再是最大值了,就必须去掉,如果队首元素(最大元素)不是滑动窗口被抛弃的元素,则继续充当

linux部署页面内容

/bin:该目录包含了常用的二进制可执行文件,如ls、cp、mv、rm等等。/boot:该目录包含了启动Linux系统所需的文件,如内核文件和引导加载程序。/dev:该目录包含了所有设备文件,如硬盘、光驱、鼠标、键盘等等。/etc:该目录包含了系统的配置文件,如网络配置、用户账户、安全设置等等。/home:该目录是所有

PT@Bernoulli概型@古典概型之伯努利概型

文章目录abstract伯努利概型伯努利试验n重伯努利试验例样本空间样本空间的重要划分成功k次的n重Bernoulli试验例例例abstractBernoulli概型是结合独立事件和n重Bernoulli试验概念的古典概型伯努利概型Bernoulli概型是基于bernoulli试验的一类古典概型这类概型的等可能性体现在

定时任务框架-xxljob

1.定时任务spring传统的定时任务@Scheduled,但是这样存在这一些问题:做集群任务的重复执行问题cron表达式定义在代码之中,修改不方便定时任务失败了,无法重试也没有统计如果任务量过大,不能有效的分片执行解决这些问题的方案为:xxl-job分布式任务调度框架2.分布式任务调度2.1什么是分布式任务调度当前软

R语言贝叶斯非参数模型:密度估计、非参数化随机效应META分析心肌梗死数据...

全文链接:http://tecdat.cn/?p=23785最近,我们使用贝叶斯非参数(BNP)混合模型进行马尔科夫链蒙特卡洛(MCMC)推断(点击文末“阅读原文”获取完整代码数据)。概述相关视频在这篇文章中,我们通过展示如何使用具有不同内核的非参数混合模型进行密度估计。在后面的文章中,我们将采用参数化的广义线性混合模

idea快捷键

目录前言一.Ctrl相关二.Alt相关三.Shift相关四.Ctrl+Alt相关五.Ctrl+Shift相关六.Alt+Shift相关七.其他汇总前言IDEA中提供了很多快捷键,点击File-->Settings-->keymap便可进入看到IDEA提供的快捷键。我们也可以搜索和自定义所有快捷键,下面给出的是IDEA中

uniapp瀑布流布局写法

首先我们要清楚瀑布流是什么?瀑布流布局(WaterfallFlowLayout),也称为瀑布流式布局,是一种常见的网页或移动应用布局方式,特点是元素以不规则的方式排列,就像瀑布中的流水一样,每个元素的高度可以不同。主要特点和优点包括:不规则的排列:瀑布流布局允许元素以不同的高度和宽度排列,因此适用于展示不同尺寸和形状的

热文推荐