根据后序-中序遍历结果 来还原一颗二叉树

后序-中序-二叉树还原
对于这样一个二叉树
           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就行了,这样的话就简单多了。

  1. #include <iostream>
  2. #include <stack>
  3. #include <string>
  4. #include <list>
  5. using   namespace  std;
  6. struct  TreeNode {
  7.      char             data;
  8.     TreeNode*   lChild;
  9.     TreeNode*   rChild;
  10. public :
  11.     TreeNode( char  c) : data(c), lChild(0), rChild(0) { }
  12. };
  13. struct  Helper {
  14.     TreeNode*   node;
  15.      int          index;
  16. public :
  17.     Helper(TreeNode* pNode,  int  idx) 
  18.         : node(pNode), index(idx) { }
  19. };
  20. int  main() {
  21.      void  inorderTraversal(TreeNode* pTree);
  22.      void  postorderTraversal(TreeNode* pTree);
  23.      void  Post_Mid_Restore(string post, string mid, TreeNode*& result);
  24.      /*                 A
  25.                     /        /
  26.                    B        C
  27.                  /   /      /    /
  28.                 D  E     F     G
  29.                / /   /  /  /   /   /  /
  30.               H I J KM N O P
  31.     */
  32.     string Postorder1 =  "HIDJKEBMNFOPGCA" ;
  33.     string Midorder1 =  "HDIBJEKAMFNCOGP" ;
  34.         
  35.    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);
  36.    
  37.     cin.get();
  38. }
  39. void  print(list<Helper>& h) {
  40.     list<Helper>::iterator iter = h.begin();
  41.      for (; iter != h.end(); iter++) {
  42.          if ((*iter).node == 0) {
  43.             cout << 0 <<  ':'  << (*iter).index <<  "; " ;
  44.         }
  45.          else  {
  46.             cout << (*iter).node->data <<  ':'  << (*iter).index <<  "; " ;
  47.         }
  48.     }
  49.     cout << endl;
  50. }
  51. //后序-中序-二叉树还原 
  52. void  Post_Mid_Restore(string post, string mid, TreeNode*& result) {
  53.      int  pi = post.size() - 1;        //后序遍历所得字符串的下标 
  54.      int  mi = 0;      //中序遍历所得字符串的下标
  55.      char  pc;                 //后序遍历的字符 
  56.     
  57.     result =  new  TreeNode(post[pi]);     //后序遍历的第一个字符是根节点 
  58.     TreeNode* pNode = 0;
  59.     
  60.     mi = mid.find(post[pi]);                 //在中序字符串中找到后序字符串的当前字符位置 
  61.     
  62.     list<Helper> helper;                    
  63.     helper.push_back(Helper(0, -1));        
  64.     helper.push_back(Helper(result, mi));
  65.     helper.push_back(Helper(0, mid.size()));
  66.     list<Helper>::iterator cur = helper.begin();
  67.     cur++;
  68.      /*
  69.         下标      -1      7       15
  70.         节点      0       A       0
  71.         cur                  ^
  72.     */
  73.      for (pi = post.size() - 2; pi >= 0 ; pi--) {
  74.         pc = post[pi];           //后序字符串的当前字符 
  75.         mi = mid.find(pc);   //在中序字符串中的位置 
  76.          while ( true ) {
  77.              if  (mi > (*cur).index) {     //在右边就是右孩子 
  78.                 pNode =  new  TreeNode(pc);
  79.                 (*cur).node->rChild = pNode;
  80.                 cur++;
  81.                 cur = helper.insert(cur, Helper(pNode, mi));
  82.                  break ;  
  83.             }
  84.              else  { //不在右边
  85.                 list<Helper>::iterator nxt = cur;
  86.                 cur--;
  87.                  if ((*cur).index < mi && mi < (*nxt).index) {     //在中间就是左孩子 
  88.                     pNode =  new  TreeNode(pc);
  89.                     (*nxt).node->lChild = pNode;
  90.                     helper.erase(nxt);
  91.                     cur++;
  92.                     cur = helper.insert(cur, Helper(pNode, mi));
  93.                      break ;
  94.                 }    //不在中间就不是左孩子 
  95.                  else  {
  96.                     helper.erase(nxt);
  97.                      continue ;
  98.                 }
  99.             }   
  100.         }
  101.     }
  102. }
  103. //中序遍历 
  104. void  inorderTraversal(TreeNode* pTree) {
  105.     stack<TreeNode*> treeStack;
  106.      do  {
  107.          while (pTree != 0) {
  108.             treeStack.push(pTree);
  109.             pTree = pTree->lChild;
  110.         }
  111.          if (!treeStack.empty()) {
  112.             pTree = treeStack.top();
  113.             treeStack.pop();
  114.             cout << pTree->data;
  115.             pTree = pTree->rChild;
  116.         }
  117.     } while (!treeStack.empty() || pTree != 0);
  118.     cout << endl;
  119. }
  120. //后序遍历 
  121. //后序遍历的辅助结构
  122. struct  postorderHelper {
  123.     TreeNode* ptr;
  124.      int  flag;
  125. public :
  126.     postorderHelper(TreeNode* pTree) : ptr(pTree), flag(0) { }
  127. }; 
  128. void  postorderTraversal(TreeNode* pTree) {
  129.     stack<postorderHelper> treeStack;
  130.      do  {
  131.          while (pTree != 0) {
  132.             treeStack.push(postorderHelper(pTree));
  133.             pTree = pTree->lChild;
  134.         }
  135.          if (!treeStack.empty()) {
  136.              if (treeStack.top().flag == 0) {
  137.                 treeStack.top().flag++;
  138.                 pTree = treeStack.top().ptr->rChild;
  139.             }
  140.              else  {
  141.                 cout << treeStack.top().ptr->data;
  142.                 treeStack.pop();
  143.                 pTree = 0;
  144.             }
  145.         }
  146.     } while (!treeStack.empty());
  147.     cout << endl;
  148. }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值