【问题描写叙述】

二叉树
           A
       /       /
       B       C
     /   /   /   /
     D   E   F   G
    / / / / / / / /
    H I J K M N O P
后序遍历的结果是:HIDJKEBMNFOPGCA,我们称之为POST
中序遍历的结果是:HDIBJEKAMFNCOGP,我们称之为MID

【算法思想】
 (1)pi指向POST的最后一个字符
 (2)用pi从POST中取一个字符pc
 (3)查找pc在MID中出现的位置mi
 (4)依据mi确定pc与前一个字符的关系(左孩子/右孩子/没有关系)
 (5)pi-1
 (6)重复重复(2)~(5)步,直到pi < 0

能够看到,问题的关键在于步骤(4),即怎样确定pc与前一个字符的关系。
在这里我们要用到两个辅助结构:
 (1)一个链表,存放Helper结构
 (2)一个Helper结构,用于记录每个节点在MID中的下标
链表我们能够用STL的list,Helper的结构例如以下

struct Helper {
TreeNode* node;
int index;
public:
Helper(TreeNode* pNode, int idx)
: node(pNode), index(idx) { }
};

当然。二叉树的节点也要有:

struct TreeNode {char    data;TreeNode*  lChild;TreeNode*  rChild;
public:TreeNode(char c) : data(c), lChild(0), rChild(0) { }
};

好了,我们一步一步来看看怎样解决这个还原二叉树的问题
(1) (A, 7)
 取POST第一个字符。然后通过Helper放入list中,并构造出一个list的初始环境
       0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
 POST: H I D J K E B M N F  O  P  G  C  A
 MID:  H D I B J E K A M F  N  C  O  G  P
 【list】
 nod  0   A   0
 idx -1   7  15
 cur      ^
 nxt
 cur, nxt都是指向list中元素的指针。头尾两个元素表示边界
(2) (C, 11)
 取POST第13个字符。依据list来判定它为谁的左孩子/右孩子
 能够看到。pc=C,其在MID中的下标mi为11,简略为(C, 11),以后都这么简略
 (C, 11)在(A, 7)右边,由于11 > 7,所以C为A的右孩子,并插入到list中,注意
 cur指针的变动。
       0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
 POST: H I D J K E B M N F  O  P  G  C  A
 MID:  H D I B J E K A M F  N  C  O  G  P
 【list】
 nod  0   A   C   0
 idx -1   7  11  15
 cur          ^
 nxt
(3) (G, 13) 
 (G, 13)在(C, 11)右边,由于 13 > 11。所以G是C的右孩子。插入list中
 【list】
 nod  0   A   C   G   0
 idx -1   7  11  13  15
 cur              ^
 nxt
以下省略。由于这个和我的另外一篇文章《前序-中序二叉树还原》非常像,仅仅只是一个从前往后
一个从后往前,另一个先确定左孩子,一个先确定右孩子罢了
以下讲一下当A的右子树都还原好了,如何还原B的情况。同一时候也解说了如何还原左孩子的方法。

(12)(B, 3) 
 这个时候链表应该是这种
 【list】
 nod  0   A   M   0
 idx -1   7   8  15
 cur          ^
 nxt 
 (B, 3)不在(M, 8)右边,nxt = cur, cur--
 【list】
 nod  0   A   M   0
 idx -1   7   8  15
 cur      ^
 nxt          ^ 
 (B, 3)不在(A, 7),(M, 8)中间。删除(M, 8)
 【list】
 nod  0   A   0
 idx -1   7  15
 cur      ^
 nxt 
(13)(B, 3) 
 此时(B, 3)不在(A, 7)右边。nxt = cur, cur--
 【list】
 nod  0   A   0
 idx -1   7  15
 cur  ^
 nxt      ^
 (B, 3)在(0, -1),(A, 7)中间,因此B是A的左孩子,删除A,插入B,cur指向B
 【list】
 nod  0   B   0
 idx -1   3  15
 cur      ^
 nxt

对了,千万不要忘记在确定X节点是Y节点的左/右孩子后要做对应的链接操作。

以下给出算法的C++表示。这里我们用iterator来表示cur, nxt指针。

我们之所以要用list是
由于list在插入/删除元素后iterator不会失效。另一点,由于list<>::iterator不支持
random access。所以我们要用nxt, cur两个iterator表示一前一后,否则的话直接用cur和
cur - 1即可了,这种话就简单多了。
【源代码实现】

#include <iostream>
#include <stack>
#include <string>
#include <list>
using   namespace  std;
struct  TreeNode {char             data;TreeNode*   lChild;TreeNode*   rChild;
public :TreeNode( char  c) : data(c), lChild(0), rChild(0) { }
};
struct  Helper {TreeNode*   node;int          index;
public :Helper(TreeNode* pNode,  int  idx) : node(pNode), index(idx) { }
};
int  main() {void  inorderTraversal(TreeNode* pTree);void  postorderTraversal(TreeNode* pTree);void  Post_Mid_Restore(string post, string mid, TreeNode*& result);/*                 A/        /B        C/   /      /    /D  E     F     G/ /   /  /  /   /   /  /H I J KM N O P*/string Postorder1 =  "HIDJKEBMNFOPGCA" ;string Midorder1 =  "HDIBJEKAMFNCOGP" ;string Postorder2 = "DBFGECA";string Midorder2 = "BDAFEGC"; TreeNode* res = 0;Post_Mid_Restore(Postorder1, Midorder1, res);inorderTraversal(res);postorderTraversal(res);Post_Mid_Restore(Postorder2, Midorder2, res);inorderTraversal(res);postorderTraversal(res);cin.get();
}
void  print(list<Helper>& h) {list<Helper>::iterator iter = h.begin();for (; iter != h.end(); iter++) {if ((*iter).node == 0) {cout << 0 <<  ':'  << (*iter).index <<  "; " ;}else  {cout << (*iter).node->data <<  ':'  << (*iter).index <<  "; " ;}}cout << endl;
}
//后序-中序-二叉树还原
void  Post_Mid_Restore(string post, string mid, TreeNode*& result) {int  pi = post.size() - 1;        //后序遍历所得字符串的下标 int  mi = 0;      //中序遍历所得字符串的下标char  pc;                 //后序遍历的字符 result =  new  TreeNode(post[pi]);     //后序遍历的第一个字符是根节点 TreeNode* pNode = 0;mi = mid.find(post[pi]);                 //在中序字符串中找到后序字符串的当前字符位置 list<Helper> helper;                    helper.push_back(Helper(0, -1));        helper.push_back(Helper(result, mi));helper.push_back(Helper(0, mid.size()));list<Helper>::iterator cur = helper.begin();cur++;/*下标      -1      7       15节点      0       A       0cur                  ^*/for (pi = post.size() - 2; pi >= 0 ; pi--) {pc = post[pi];           //后序字符串的当前字符 mi = mid.find(pc);   //在中序字符串中的位置 while ( true ) {if  (mi > (*cur).index) {     //在右边就是右孩子 pNode =  new  TreeNode(pc);(*cur).node->rChild = pNode;cur++;cur = helper.insert(cur, Helper(pNode, mi));break ;  }else  { //不在右边list<Helper>::iterator nxt = cur;cur--;if ((*cur).index < mi && mi < (*nxt).index) {     //在中间就是左孩子 pNode =  new  TreeNode(pc);(*nxt).node->lChild = pNode;helper.erase(nxt);cur++;cur = helper.insert(cur, Helper(pNode, mi));break ;}    //不在中间就不是左孩子 else  {helper.erase(nxt);continue ;}}   }}
}
//中序遍历
void  inorderTraversal(TreeNode* pTree) {stack<TreeNode*> treeStack;do  {while (pTree != 0) {treeStack.push(pTree);pTree = pTree->lChild;}if (!treeStack.empty()) {pTree = treeStack.top();treeStack.pop();cout << pTree->data;pTree = pTree->rChild;}} while (!treeStack.empty() || pTree != 0);cout << endl;
}
//后序遍历
//后序遍历的辅助结构
struct  postorderHelper {TreeNode* ptr;int  flag;
public :postorderHelper(TreeNode* pTree) : ptr(pTree), flag(0) { }
};
void  postorderTraversal(TreeNode* pTree) {stack<postorderHelper> treeStack;do  {while (pTree != 0) {treeStack.push(postorderHelper(pTree));pTree = pTree->lChild;}if (!treeStack.empty()) {if (treeStack.top().flag == 0) {treeStack.top().flag++;pTree = treeStack.top().ptr->rChild;}else  {cout << treeStack.top().ptr->data;treeStack.pop();pTree = 0;}}} while (!treeStack.empty());cout << endl;
}
<span style="font-family:Times New Roman;font-size:18px;">struct Helper {TreeNode* node;int   index;
public:Helper(TreeNode* pNode, int idx) : node(pNode), index(idx) { }
};</span>

当然,

数据结构基础 后序遍历和中序遍历还原二叉树相关推荐

  1. 数据结构与算法之二叉树的先序遍历,中序遍历,后序遍历

    数据结构与算法之二叉树的先序遍历,中序遍历,后移遍历 目录 实现二叉树的先序,中序,后序遍历,包括递归方式和非递归方式 在二叉树中找到一个节点的后继节点 1. 实现二叉树的先序,中序,后序遍历,包括递 ...

  2. c++ 删除二叉树的子树_数据结构—树|二叉树|前序遍历、中序遍历、后序遍历【图解实现】...

    点击蓝字关注我们 AI研习图书馆,发现不一样的精彩世界 数据 结构 二叉树的遍历 一.树 在谈二叉树的知识点之前,我们首先来看一下树和图的基本概念.树:不包含回路的连通无向图,树是一种简单的非线性结构 ...

  3. 【数据结构笔记10】二叉树的先序、中序、后序遍历,中序遍历的堆栈/非递归遍历算法,层序遍历,确定一个二叉树,树的同构

    本次笔记内容: 3.3.1 先序中序后序遍历 3.3.2 中序非递归遍历 3.3.3 层序遍历 3.3.4 遍历应用例子 小白专场:题意理解及二叉树表示 小白专场:程序框架.建树及同构判别 文章目录 ...

  4. 数据结构之前序遍历,中序遍历,后序遍历

      前序遍历->首先访问根结点, 然后遍历左子树, 最后遍历右子树. 在遍历左.右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树. 中序遍历->首先遍历左子树, 然后访问 ...

  5. 数据结构之二叉树的前序遍历、中序遍历、后序遍历、层序遍历

    一.二叉树的遍历概念 二叉树的遍历是指从根结点触发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次. (1). 前(先)序遍历 前序遍历:若二叉树为空,则空操作返回,否则先 ...

  6. 二叉树的前序遍历、中序遍历、后序遍历

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 为什么需要树这种数据结构 树的常用术语 二叉树的概念 二叉树遍历的说明 实现二叉树 实现二叉树的遍历 二叉树查找结点 二叉树 ...

  7. python实现二叉树遍历(前序遍历、中序遍历、后序遍历)

    python实现二叉树遍历(前序遍历.中序遍历.后序遍历) 在计算机科学中,二叉树是一种树数据结构,其中每个节点最多有两个子节点,称为左子节点和右子节点.使用集合理论概念的递归定义是(非空)二叉树是元 ...

  8. 二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现

    二叉树,二叉树的归先序遍历,中序遍历,后序遍历,递归和非递归实现 提示:今天开始,系列二叉树的重磅基础知识和大厂高频面试题就要出炉了,咱们慢慢捋清楚! 文章目录 二叉树,二叉树的归先序遍历,中序遍历, ...

  9. 由先序遍历和中序遍历得到后序遍历和层次遍历(二叉树)

    前几天写了1020 Tree Traversals (25 分)-PAT甲级这个题目,明白了如何由二叉树的后序遍历和中序遍历得到先序遍历和层次遍历.受这道题启发,思考了一下如何由二叉树的先序遍历和中序 ...

  10. 二叉树的前序遍历,中序遍历,后序遍历-详解-配套例题

    二叉树作为数据结构中一种简单而且重要的数据结构,他的存储结构和算法都相对比较简单,因此他也显得特别重要,因为很多问题都可以抽象为二叉树的问题. 在这里我们对于二叉树的基本概念不做详细介绍,我们这里主要 ...

最新文章

  1. java8避免null_在 Java 8 中避免 Null 检查
  2. Spark1.2新特性概述
  3. Centos最小化装机网络问题
  4. 查看NVIDIA使用率工具目录
  5. 随想录(文件系统的第一个用户程序shell)
  6. 二分查找算法的C/C++实现
  7. Oracle学习笔记之五sp1,PL/SQL之BULK COLLECT
  8. 实战开发经验:是什么阻碍了我们跨平台
  9. 开机进入boot menu和application menu,无法开机
  10. IT小公司避坑及生存指南
  11. JavaWeb项目—— 博客系统
  12. java insert方法_Java StringBuilder insert()方法
  13. # 书籍《银河帝国3:第二基地》读后感-20211018
  14. openbci/bciduino脑电放大器lsl数据解释
  15. linux之vim下载及编写规则
  16. LeetCode第9题 回文数(Palindrome Number)
  17. ef mysql 约定_EF 数据库连接约定(Connection String Conventions in Code First)
  18. Linux系统必学必会知识点整理
  19. 浅谈欧拉定理及其扩展
  20. 照片恢复软件哪个好用?5个好用的照片恢复软件推荐

热门文章

  1. Golang生成C动态库.so和静态库.a
  2. Dockerfile语法简介(精)
  3. python删除、替换字符串某字符后的字符串(删除字符串、替换字符串、strip、split、rstrip、lstrip、replace)
  4. 【转】oracle数据库NUMBER数据类型
  5. 2016年5月30日上午(传智Bootstrap笔记六(图片样式))
  6. Windows 7安装PlayReady出现“任务被禁用”错误信息
  7. 仅靠“小于运算“生存的map
  8. python编程多行输入_python多行输入的方法有哪些
  9. 一文讲清,MySQL如何解决多事务并发问题
  10. 爬取了BOSS直聘、拉勾等近1000+招聘需求,总结出3年+Java开发的高频技术需求