IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 《算法笔记》读书记录DAY_32 -> 正文阅读

[数据结构与算法]《算法笔记》读书记录DAY_32

CHAPTER_9? 提高篇(3)——数据结构(2)

9.2.5 重建二叉树

首先我们要给出一个关于二叉树的结论:给定一个中序遍历序列和先序遍历序列,可以确定一颗二叉树;给定中序遍历和后序遍历,也可以确定一个二叉树;给定中序遍历和层序遍历,同样可以确定二叉树。而后序遍历、先序遍历、层序遍历中任意组合,都不能确定二叉树。

由此这节我们来解决一个重要问题:给定一个二叉树的先序遍历和中序遍历,如何重建这颗二叉树。已知先序序列为pre1、pre2、... 、pren,中序序列为in1、in2、... 、inn。我们接下来对该问题进行分析。

解决这个问题我们先要理解先序和中序遍历的性质。先序遍历序列有一个重要的性质:序列的第一个结点为当前树的根。这根据先序遍历的过程我们很容易理解。因此对于给定的树,根节点即为pre1。中序序列同样有一条有趣的性质:假若已知当前树的根节点为ink,则序列被分为两部分,in1到ink-1为根节点的左子树,ink+1到inn为根节点的右子树。即如下图所示:

通过上面的性质,我们得以确定树的三部分:根节点、左子树的先序和中序序列、右子树的先序和中序序列。接下来我们对左子树的序列用同样的操作重建左子树,右子树也同样。如此一直递归下去,递归边界是什么呢?答案很显然,当前递归的树为空,即给定序列长度小于等于0时,递归结束。通过递归的方法,我们得以重建这颗二叉树。

下面通过一道例题,来练习这个思路的代码实现。

题目:?

给出一颗二叉树的先序遍历序列和中序遍历序列。求这颗二叉树的层序遍历序列。

输入样例:

6? ? ? ? ? ? ? ? ? ? ? ? //结点个数

1 2 4 5 3 6? ? ? ? ?//先序序列

4 2 5 1 3 6? ? ? ? ?//中序序列

输出样例:

1 2 3 4 5 6

思路:

首先由给定序列重建二叉树,再对二叉树层序遍历即可。由于在上一节已经给出层序遍历代码,这里只讲解重建二叉树。

先序序列的第一个元素pre1,是当前树的根节点。我们遍历中序序列找到下标ink=pre1,得而确定根节点在中序序列的位置。因此中序序列被ink分为两部分,左子树的结点个数为ink-1,左子树在先序序列中对应的区间为[2,ink],在中序序列中对应的区间为[1,ink-1];右子树个数为n-ink,在先序序列中对应的区间为[ink+1,n],在中序序列中对应的区间为[ink+1,n]。至此第一轮递归已经完成,我们得到了根节点,然后对左子树和右子树的序列进行递归。

现在来看递归过程的一般情况。假设当前递归的先序序列为[preL,preR],中序序列为[inL,inR]。我们遍历中序序列得到当前根节点的下标k,那么左子树的结点个数为k-inL,其先序序列为[preL+1,preL+k-inL]、中序序列为[inL,k-1];右子树的结点个数为inR-k,其先序序列为[preL+k-inL+1,preR]、中序序列为[k+1,inR]。

通过一层层的递归,我们要确定递归边界。当preR-preL<0或者inR-inL<0时,说明序列长度小于等于0,则退出。

代码如下:

#include<iostream>
#include<queue>

using namespace std;
const int maxn=100;
int preOrder[maxn],inOrder[maxn];      //分别存储先序和中序序列 

struct node {
	int data;
	node *lchild,*rchild;
};

node* create(int preL,int preR,int inL,int inR) {     //先序序列[preL,preR],中序序列[inL,inR] 
	if(preR-preL<0) {
		return NULL;
	}
	node *root=new node;                              //申请新节点
	root->data=preOrder[preL];
	int k; 
	for(k=inL;k<=inR;k++) {                           //找到中序序列中根节点的下标位置       
		if(inOrder[k]==preOrder[preL])
			break;
	}
	root->lchild=create(preL+1,preL+k-inL,inL,k-1);   //递归构建左子树
	root->rchild=create(preL+k-inL+1,preR,k+1,inR);   //递归构建右子树
	return root;
}

void BFS(node* root) {                            //层序遍历 
	queue<node*> q;
	q.push(root);
	while(!q.empty()) {
		node *now=q.front();
		q.pop();
		cout<<now->data<<' ';
		if(now->lchild)
			q.push(now->lchild);
		if(now->rchild)
			q.push(now->rchild);
	}
}

int main() {
	int n;
	cin>>n;
	for(int i=0;i<n;i++) {
		cin>>preOrder[i];
	}
	for(int i=0;i<n;i++) {
		cin>>inOrder[i];
	}
	node *root=NULL;                   //创建根节点 
	root=create(0,n-1,0,n-1);          //重建二叉树 
	BFS(root);                         //层序遍历 
	return 0;
}

解决了中序和先序重建二叉树的问题,我们也同时解决了中序和后序的重建问题。因为后序和中序重建的思路和上面相同,唯一的区别在于:后序序列的最后一个结点为当前树的根节点。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-10-20 12:44:30  更:2021-10-20 12:46:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:45:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码