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

来源:互联网 发布:手机文件查看软件 编辑:程序博客网 时间:2024/05/16 10:21

前序-中序-二叉树还原
对于这样一个二叉树
         A
     /       /
     B       C
   /   /   /   /
   D   E   F   G
  / / / / / / / /
  H I J K M N O P

 

前序遍历的结果是:ABDHIEJKCFMNGOP,我们称之为PRE
中序遍历的结果是:HDIBJEKAMFNCOGP,我们称之为MID
还原的方法是:
 (1)pi指向PRE的第一个字符
 (2)用pi从PRE中取一个字符pc
 (3)查找pc在MID中出现的位置mi
 (4)根据mi确定pc与前一个字符的关系(左孩子/右孩子/没有关系)
 (5)pi+1
 (6)反复重复(2)~(5)步,直到pi超过了PRE的长度

可以看到,问题的关键在于步骤(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)
     取PRE第一个字符,然后通过Helper放入list中,并构造出一个list的初始环境
      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
 PRE: A B D H I E J K C F  M  N  G  O  P
 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      ^
 prv
 cur, prv都是指向list中元素的指针,头尾两个元素表示边界
(2) (B, 3)
     取PRE第二个字符,根据list来判定它为谁的左孩子/右孩子
 可以看到,pc=B,其在MID中的下标mi为3,简略为(B, 3),以后都这么简略
 (B, 3)在(A, 7)左边,因为3 < 7,所以B为A的左孩子,并插入到list中,注意
 cur指针的变动,以后每次插入元素后cur指针都会变动。

      0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
 PRE: A B D H I E J K C F  M  N  G  O  P
 MID: H D I B J E K A M F  N  C  O  G  P
 【list】
 nod  0   B   A   0
 idx -1   3   7  15
 cur      ^
 prv
(3) (D, 1)
     (D, 1)在(B, 3)左边,因为 1 < 3,所以D是B的左孩子,插入list中
 【list】
 nod  0   D   B   A   0
 idx -1   1   3   7  15
 cur      ^
 prv 
(4) (H, 0)
     (H, 0)在(D, 1)左边,H是D的左孩子,插入list中
 【list】
 nod  0   H   D   B   A   0
 idx -1   0   1   3   7  15
 cur      ^
 prv 
(5) (I, 2)
    (I, 2)不在(H, 0)的左边,因为H已经是最左边了,于是我们要prv = cur,cur++
 【list】
 nod  0   H   D   B   A   0
 idx -1   0   1   3   7  15
 cur          ^
 prv      ^
 (I, 2)不在(H, 0),(D, 1)中间,因为0 < 2 < 1不成立,把prv指向的(H, 0)删除
(6) (I, 2)
     (I, 2)不在(D, 1)左边,prv = cur, cur++
 【list】
 nod  0   D   B   A   0
 idx -1   1   3   7  15
 cur          ^
 prv      ^
     (I, 2)在(D, 1),(B, 3)中间,因为1 < 2 < 3,所以I是D的右孩子
 然后把D删除,把I插入,cur指向I
 【list】
 nod  0   I   B   A   0
 idx -1   2   3   7  15
 cur      ^
 prv     
(7) (E, 5)
     (E, 5)不在(I, 2)左边,prv = cur, cur++
 【list】
 nod  0   I   B   A   0
 idx -1   2   3   7  15
 cur          ^
 prv      ^
     结果(E, 5)不在(I, 2),(B, 3)中间,把prv指向的(I, 2)删除
(8) (E, 5)
     (E, 5)不在(B, 3)左边,prv = cur, cur++
 【list】
 nod  0   B   A   0
 idx -1   3   7  15
 cur          ^
 prv      ^
 (E, 5)在(B, 3),(A, 7)中间,于是E是B的右孩子,删除B,插入E,cur指向E
 【list】
 nod  0   E   A   0
 idx -1   5   7  15
 cur      ^
 prv     
(9) (J, 4)
 (J, 4)是在(E, 5)左边的,于是插入(J, 4),cur指向(J, 4)
 【list】
 nod  0   J   E   A   0
 idx -1   4   5   7  15
 cur      ^
 prv 
(10)(K, 6)
 (K, 6)不在(J, 4)左边,prv = cur, cur++
 (K, 6)不在(J, 4),(E, 5)中间,因此删除(J, 4)
 【list】
 nod  0   E   A   0
 idx -1   5   7  15
 cur      ^
 prv
(11)(K, 6)
 (K, 6)不在(E, 5)左边,prv = cur, cur++
 (K, 6)在(E, 5),(A, 7)中间,于是K是E的右孩子,删除E,插入K,cur指向K
 【list】
 nod  0   K   A   0
 idx -1   6   7  15
 cur      ^
 prv 
***到此为止,A的左子树都已经还原完成了,右子树的还原和左子树一样,以下我只给出
***C的确定方法,其余就省略了。
(12)(C, 11)
     (C, 11)不在(K, 6)左边,prv = cur, cur++
     (C, 11)不在(K, 6),(A, 7)中间,删除(K, 6)
 【list】
 nod  0   A   0
 idx -1   7  15
 cur      ^
 prv 
(13)(C, 11)
     (C, 11)不在(A, 7)左边,prv = cur, cur++
     (C, 11)在(A, 7),(0, 15)中间,于是C是A的右孩子,删除A,插入C,cur指向C
 【list】
 nod  0   C   0
 idx -1   11  15
 cur      ^
 prv
以下省略。。。。 
对了,千万不要忘记在确定X节点是Y节点的左/右孩子后要做相应的链接操作哦。

下面给出算法的C++表示,这里我们用iterator来表示cur, prv指针。我们之所以要用list是
因为list在插入/删除元素后iterator不会失效。还有一点,因为list<>::iterator不支持
random access,所以我们要用prv, 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 preorderTraversal(TreeNode* pTree);
  22.     void inorderTraversal(TreeNode* pTree);
  23.     void Pre_Mid_Restore(string pre, string mid, TreeNode*& result);
  24.     /*                 A
  25.                     /      /
  26.                    B       C
  27.                  /   /   /   /
  28.                  D   E   F   G
  29.                 / / / / / / / /
  30.                 H I J K M N O P
  31.     */
  32.     string Preorder1 = "ABDHIEJKCFMNGOP";
  33.     string Midorder1 = "HDIBJEKAMFNCOGP";
  34.     
  35.     string Preorder2 = "ABDFCEG";
  36.     string Midorder2 = "BFDAEGC";
  37.     string Preorder3 = "ABDCEFG";
  38.     string Midorder3 = "DBAFEGC";
  39.         
  40.     TreeNode* res = 0;
  41.     
  42.     Pre_Mid_Restore(Preorder1, Midorder1, res);
  43.     preorderTraversal(res);
  44.     inorderTraversal(res);
  45.     
  46.     Pre_Mid_Restore(Preorder2, Midorder2, res);
  47.     preorderTraversal(res);
  48.     inorderTraversal(res);
  49.     Pre_Mid_Restore(Preorder3, Midorder3, res);
  50.     preorderTraversal(res);
  51.     inorderTraversal(res);
  52.         
  53.     cin.get();
  54. }
  55. //前序-中序-二叉树还原 
  56. void Pre_Mid_Restore(string pre, string mid, TreeNode*& result) {
  57.     size_t pi = 0;      //前序遍历所得字符串的下标 
  58.     size_t mi = 0;      //中序遍历所得字符串的下标
  59.     char pc;                //前序遍历的字符 
  60.     
  61.     result = new TreeNode(pre[pi]); //前序遍历的第一个字符是根节点 
  62.     TreeNode* pNode = 0;
  63.     
  64.     mi = mid.find(pre[pi]);             //在中序字符串中找到前序字符串的当前字符位置 
  65.     
  66.     list<Helper> helper;                    
  67.     helper.push_back(Helper(0, -1));        
  68.     helper.push_back(Helper(result, mi));
  69.     helper.push_back(Helper(0, mid.size()));
  70.     list<Helper>::iterator cur = helper.begin();
  71.     cur++;
  72.     /*
  73.         下标      -1      7       15
  74.         节点      0       A       0
  75.         iter                ^
  76.     */
  77.     for(pi = 1; pi < pre.size(); pi++) {
  78.         pc = pre[pi];           //前序字符串的当前字符 
  79.         mi = mid.find(pc);  //在中序字符串中的位置 
  80.         while(true) {
  81.             if (mi < (*cur).index) {    //在左边就是左孩子 
  82.                 pNode = new TreeNode(pc);
  83.                 (*cur).node->lChild = pNode;
  84.                 cur = helper.insert(cur, Helper(pNode, mi));
  85.                 break;  
  86.             }
  87.             else {  //不在左边 
  88.                 list<Helper>::iterator prv = cur;
  89.                 cur++;
  90.                 if((*prv).index < mi && mi < (*cur).index) {    //在中间就是右孩子 
  91.                     pNode = new TreeNode(pc);
  92.                     (*prv).node->rChild = pNode;
  93.                     helper.erase(prv);
  94.                     cur = helper.insert(cur, Helper(pNode, mi));
  95.                     break;
  96.                 }   //不在中间就不是右孩子 
  97.                 else {
  98.                     helper.erase(prv);
  99.                     continue;
  100.                 }
  101.             }
  102.             //some erase work   
  103.         }
  104.     }
  105. }
  106. //前序遍历 
  107. void preorderTraversal(TreeNode* pTree) {
  108.     stack<TreeNode*> treeStack;
  109.     do {
  110.         while(pTree != 0) {
  111.             cout << pTree->data;
  112.             if(pTree->rChild != 0) {
  113.                 treeStack.push(pTree->rChild);
  114.             }
  115.             pTree = pTree->lChild;
  116.         }
  117.         if(!treeStack.empty()) {
  118.             pTree = treeStack.top();
  119.             treeStack.pop();
  120.         }
  121.     }while(!treeStack.empty() || pTree != 0);
  122.     cout << endl;
  123. }
  124. //中序遍历 
  125. void inorderTraversal(TreeNode* pTree) {
  126.     stack<TreeNode*> treeStack;
  127.     do {
  128.         while(pTree != 0) {
  129.             treeStack.push(pTree);
  130.             pTree = pTree->lChild;
  131.         }
  132.         if(!treeStack.empty()) {
  133.             pTree = treeStack.top();
  134.             treeStack.pop();
  135.             cout << pTree->data;
  136.             pTree = pTree->rChild;
  137.         }
  138.     }while(!treeStack.empty() || pTree != 0);
  139.     cout << endl;
  140. }