赞
踩
提示:本文研究的对象是已知中后序序列 还原二叉树 已知树的结构为递归型,可用递归方式创建树 可用递归方式遍历树 同样也可用递归方式还原树
二叉树的遍历在数据结构中是一种非常常见的操作,已知两个遍历的序列(必须包含中序序列) 恢复二叉树是一个重点 也是一个难点 本文讨论已知中序和后序序列 恢复二叉树
提示:以下是本篇文章正文内容
通过给定的遍历序列 从遍历序列已知的遍历序列还原为原来的二叉树称为恢复二叉树,因树采用递归的方式创建 故也可采用递归的方式恢复二叉树
如下:
前:ABDGCEF
中:DGBAECF
后:GDBEFCA
前:ABDGCEF
中:DGBAECF
以下完成已知先序和中序 还原整颗二叉树(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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。