恢复二叉树

恢复二叉树(已知中后序序列得到前序序列)

提示:本文研究的对象是已知中后序序列 还原二叉树 已知树的结构为递归型,可用递归方式创建树 可用递归方式遍历树 同样也可用递归方式还原树



前言

二叉树的遍历在数据结构中是一种非常常见的操作,已知两个遍历的序列(必须包含中序序列) 恢复二叉树是一个重点 也是一个难点 本文讨论已知中序和后序序列 恢复二叉树


提示:以下是本篇文章正文内容

一、恢复二叉树是什么?

通过给定的遍历序列 从遍历序列已知的遍历序列还原为原来的二叉树称为恢复二叉树,因树采用递归的方式创建 故也可采用递归的方式恢复二叉树

二、恢复步骤

1.给定已知的遍历序列

如下:
前:ABDGCEF
中:DGBAECF
后:GDBEFCA

2.读入数据

前:ABDGCEF
中:DGBAECF

3.实现思想和代码

以下完成已知先序和中序 还原整颗二叉树(C语言实现)
前序的根节点一定为根节点,在中序序列中找到根节点A的位置 左半边为左子树 左子树中序序列为 DGB 右半边为右子树 右子树中序序列为ECF 再依次类推 找到左右子树中序序列 回到先序序列 根据先序序列的特点 中序序列的节点在先序序列中出现的必为根节点 这样可以把一个大问题化为若干个子问题 逐一击破 最后再合并 刚好也符合递归的思想 以下给出实现的具体代码:

以下的所有操作均基于该二叉树,关于二叉树的其他基本操作,参考该文章

在还原二叉树之前需要熟悉二叉树的三种遍历 为下文的还原操作提供理论支持
讨论
前序遍历特点: 先根后左结点 再右结点 前序序列总是先输出第一个访问的结点
中序遍历特点:先访问左孩子 遇到左结点 遇到左结点为空再访问根结点 再访问右结点
后序遍历特点: 先访问左结点 左节点已访问或为空时访问右结点 又结点为空或被访问时访问根结点

参考:LeetCode官方视频(已知前序和后序恢复二叉树)
来自:LeetCode106从中序与后序遍历序列构造二叉树

以下是已知中后序 还原二叉树的算法

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
#include<queue>
#include<stdlib.h>
using namespace std;
typedef struct BiTNode {
	char data;
	struct BiTNode *lchild;
	struct BiTNode *rchild;
}*BiTree;

//中后序恢复二叉树
BiTree buildTree(char inOrder[], char postOrder[], int inLeft,
                 int inRight, int postLeft, int postRight) {
	if (postLeft > postRight) {
		return NULL;
	}
	BiTree root = (BiTNode*)malloc(sizeof(BiTNode));
	root->data=postOrder[postRight]; //根节点赋值 
	int k = 0;	//记录中序根节点位置  接下来找中序根节点
	for (int i = inLeft; i <= inRight; i++) {
		if (inOrder[i] == postOrder[postRight]) {
			k = i;
			break;
		}
	}
	int numLeft = k-inLeft;	 
    root->lchild=buildTree(inOrder,postOrder,inLeft,k-1,postLeft,postLeft+numLeft-1);
    root->rchild=buildTree(inOrder,postOrder,k+1,inRight,postLeft+numLeft,postRight-1);
	return root;
}

//先序遍历
void preOrder(BiTree T){
	if(T){
		cout<<T->data<<" ";
		preOrder(T->lchild);
		preOrder(T->rchild); 
	}
} 

//后序递归遍历二叉树
void postOrder(BiTree T) {
	if(T) {
		postOrder(T->lchild);
		postOrder(T->rchild);
		cout<<T->data<<" ";
	}
}

int main() {
	char post[60]="GDBEFCA", in[60]="DGBAECF";
	/*
	//解除注释后可自行测试需要的数据 
	cin >> in;
	cin >> post;
	*/
	int inLeft = 0, postLeft = 0;
	int inRight = strlen(in)-1, postRight = strlen(post)-1;
	BiTree T = buildTree(in, post, inLeft, inRight, postLeft, postRight);
	cout<<"后序遍历"<<endl;
	postOrder(T);
	cout<<endl;
	cout<<"先序遍历"<<endl; 
	preOrder(T); 
	return 0;
}
/*
前:ABDGCEF
中:DGBAECF
后:GDBEFCA
试错笔记:
	int numLeft = k-inLeft;	 
    root->lchild=buildTree(inOrder,postOrder,inLeft,k-1,postLeft,postLeft+numLeft-1);
    root->rchild=buildTree(inOrder,postOrder,k+1,inRight,postLeft+numLeft,postRight-1);	 
*/

核心的思想以及难点在如下程序段中:
分析:先来简单分析一个 后序遍历的顺序是左右根 即一段后序序列中 最后一个节点一定是根节点 设置postRight用于记录最后一个节点 取后序最后一个节点作为根节点 再到中序序列中找到根节点 将中序序列切分为左右子树 再将左右子树继续分割 知道还原整颗二叉树 依此类推

算法中要注意的细节 当后序序列的postLeft(序列起始位置)>postRight(序列结束位置)时 即退出递归
另外 每次需要将后序遍历最后一个节点的data域赋值给root->data 用于记录数据
再设置好一个变量k用于记录中序序列中根节点的位置 作为以下递归切割子树的依据

BiTree buildTree(char inOrder[], char postOrder[], int inLeft,
                 int inRight, int postLeft, int postRight) {
	if (postLeft > postRight) {
		return NULL;
	}
	BiTree root = (BiTNode*)malloc(sizeof(BiTNode));
	root->data=postOrder[postRight]; //根节点赋值 
	cout<<root->data<<endl;
	int k = 0;	//记录中序根节点位置  接下来找中序根节点
	for (int i = inLeft; i <= inRight; i++) {
		if (inOrder[i] == postOrder[postRight]) {
			k = i;
			break;
		}
	}
	int numLeft = k-inLeft;	//定义该变量不可省略  省略以后会出错  思考为何该变量不可省略 
    root->lchild=buildTree(inOrder,postOrder,inLeft,k-1,postLeft,postLeft+numLeft-1);
    root->rchild=buildTree(inOrder,postOrder,k+1,inRight,postLeft+numLeft,postRight-1);
	return root;
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值