二叉樹的恢復

最近筆面老碰上這類題,老忘,因此寫個備忘。

二叉樹的三種遍歷常用於恢復:先序,中序,後序。對於先+中,後+中這兩種組合,對任意二叉樹的恢復都有唯一解,但對先+後的情況則不是,這種情況下要滿足要求:對所有非葉節點,其兩個子節點都存在,也即,一個節點要麼是葉節點,要麼有兩個節點。

典型的恢復方法是遞歸建構節點的左,右子樹。一個一個看:

假設二叉樹原型如下,爲了方便,節點的值剛好等於層次遍歷索引


先序:
1,2,4,5,10,11,3,6,7,
中序:
4,2,10,5,11,1,6,3,7,
後序:
4,10,11,5,2,6,7,3,1,

先+中恢復:

對先序,注意第一個節點是根節點,其遍歷順序是中左右,因此,若把第一個節點作爲基準,則其左右子樹連續存儲在該節點之後,不過,目前我們還不知道到底左右子樹的分界點在哪,因此需要結合中序來確定其分界點。先序的第一個節點在中序的第5個位置(從0開始算),而我們知道中序的存儲順序是:先中後,因此,對中序列,該節點的左邊是其左子樹,右邊是右子樹。因此,根據節點在中序中的位置可以確定其左子樹的元素個數,對應到先序即可得到該節點的左,右子樹分別在先,中序的位置。根據上述信息就可遞歸的恢復根節點的左,右子樹,進而得到整個樹。

後+中:

與上述類似,只不對後序,根節點在末尾,其它的可依此類推。

先+後:

這種情況下恢復的二叉樹不一定有唯一解,考慮如下的樹:

         1

       /

    2

先序:1,2

後序:2,1

 1

       \

           2

先序: 1,2

後序:2,1

可看到不同的樹,先,後序遍歷是一樣的。

其唯一解的條件文章開頭已經說過了。不過沒有經過嚴格考究!

這裏只針對有唯一解的情況做討論,還考慮上圖的例子,結合實例描述如下:

先序:
1,2,4,5,10,11,3,6,7,
後序:
4,10,11,5,2,6,7,3,1,

對先序,第一個節點與後序最後節點對應,然後再看先序的第二個節點(值爲2),我們知道,如果先序存在子樹,則必同時存在左右子樹,因此可斷定,第二個節點正是根節點的左子樹節點,可先恢復成:

           1

       /

  2

而它又把後序分成兩個部分,一左一右(右邊不包括最末的根節點):左(4,10,11,5,2),右(6,7,3),說到這裏,再結合上圖,一切都明白了:“左”正是根的左子樹,“右”正是根的右子樹。於是,我們又得到了根節點的左右子樹,遞歸,搞定。

上代碼:

  1. typedef struct tagTREE  
  2. {  
  3.     int val;        //值   
  4.     tagTREE* left;  //左節點   
  5.     tagTREE* right; //右節點   
  6.     tagTREE* parent;//父節點(有時候爲了方便,不一定都定義它)   
  7.     bool isVisited; //標記該結點是否已訪問,給某些特殊操作準備   
  8. }TREE;  
  9. template<class T>  
  10. void show(T* vec,int N)  
  11. {  
  12.     for (int i=0;i<N;++i)  
  13.     {  
  14.         cout<<vec[i]<<",";  
  15.     }  
  16.     cout<<endl;  
  17. }  
  18. //按數組裏指定的層次數值,生成任意二叉樹結構,數組裏缺失的數值表示對應該層次的該節點沒有   
  19. void CreateTree(TREE** node, int a[], int N )  
  20. {  
  21.     //預處理,記錄節點在全部節點中的索引,而不是其真正位置號   
  22.     cout<<endl;  
  23.     int* arr = new int[a[N-1]];  
  24.     for (int i=0;i<a[N-1];++i)  
  25.     {  
  26.         arr[ i ] = 0;  
  27.     }  
  28.     int k=0;  
  29.     for (int i=0;i<N;++i)  
  30.     {  
  31.         arr[ a[i]-1 ] = i;  
  32.     }  
  33.   
  34.     TREE* arrTree = new TREE[N];  
  35.   
  36.     //root   
  37.     arrTree[0].parent = NULL;  
  38.   
  39.     for (int i=1;i<=N;++i)  
  40.     {  
  41.         arrTree[i-1].val = a[i-1];    
  42.         arrTree[i-1].isVisited = false;  
  43.         int parentIdx = int(a[i-1] / 2);  
  44.   
  45.         if( parentIdx == 0 )  
  46.             arrTree[i-1].parent = NULL;  
  47.         else  
  48.             arrTree[i-1].parent = &arrTree[ arr[ parentIdx-1 ] ];  
  49.   
  50.         int leftIdx = int(a[i-1] * 2 );  
  51.         int rightIdx = leftIdx + 1;  
  52.   
  53.         if (  leftIdx > a[N-1] || arr[leftIdx-1] == 0 )  
  54.         {  
  55.             arrTree[i-1].left = NULL;  
  56.         }  
  57.         else  
  58.         {  
  59.             arrTree[i-1].left = &arrTree[ arr[ leftIdx-1 ] ];  
  60.         }  
  61.   
  62.         if ( rightIdx > a[N-1] || arr[rightIdx-1] == 0 )  
  63.         {  
  64.             arrTree[i-1].right = NULL;  
  65.         }  
  66.         else  
  67.         {  
  68.             arrTree[i-1].right = &arrTree[ arr[ rightIdx-1 ] ];  
  69.         }  
  70.   
  71.     }  
  72.     *node = arrTree;  
  73.   
  74.     //test   
  75.     for (int i=1;i<=N;++i)  
  76.     {  
  77.         cout<<"val="<<arrTree[i-1].val;  
  78.         cout<<" left=";  
  79.         if (arrTree[i-1].left)  
  80.         {  
  81.             cout<<arrTree[i-1].left->val;  
  82.         }  
  83.         cout<<" right=";  
  84.         if (arrTree[i-1].right)  
  85.         {  
  86.             cout<<arrTree[i-1].right->val;  
  87.         }  
  88.         cout<<" parent=";  
  89.         if (arrTree[i-1].parent)  
  90.         {  
  91.             cout<<arrTree[i-1].parent->val;  
  92.         }  
  93.         cout<<endl;  
  94.     }  
  95. }  
  96. //先序遍歷,第一個參數是二叉樹的頭結點,第二個參數是用於記錄遍歷序列的數組,下同   
  97. void PreOrder(TREE* pTree,int** out)  
  98. {  
  99.     if( !pTree )  
  100.         return;  
  101.     *(*out)++ = pTree->val;  
  102.   
  103.     if ( pTree->left )  
  104.         PreOrder(pTree->left,out);  
  105.     if ( pTree->right )  
  106.         PreOrder(pTree->right,out);  
  107. }  
  108. void InOrder(TREE* pTree,int** out)  
  109. {  
  110.     if( !pTree )  
  111.         return;  
  112.     if ( pTree->left )  
  113.         InOrder(pTree->left,out);  
  114.     *(*out)++ = pTree->val;  
  115.     if ( pTree->right )  
  116.         InOrder(pTree->right,out);  
  117. }  
  118. void PostOrder(TREE* pTree,int** out)  
  119. {  
  120.     if( !pTree )  
  121.         return;  
  122.     if ( pTree->left )  
  123.         PostOrder(pTree->left,out);  
  124.     if ( pTree->right )  
  125.         PostOrder(pTree->right,out);  
  126.     *(*out)++ = pTree->val;  
  127. }  
  128.   
  129. //先+中恢復二叉樹   
  130. TREE* pre_in(const int* pre,const int* in, int n)  
  131. {  
  132.     if( n == 0 )  
  133.         return NULL;  
  134.   
  135.     TREE* head = new TREE();      
  136.     //先序的第一個節點是根節點   
  137.     head->val = pre[0];  
  138.     head->parent = NULL;  
  139.     //由根節點在中序的位置,基左邊是根的左子樹,右邊是右子樹   
  140.     int i=0;  
  141.     for (;i<n;++i)  
  142.     {  
  143.         if( pre[0] == in[i] )  
  144.             break;  
  145.     }  
  146.     //遞歸的構建節點的左右子樹,這裏需要確定左/右子樹對應的先序/中序段   
  147.     TREE* left  = pre_in( pre+1,in,i );  
  148.     TREE* right = pre_in( pre+i+1,in+i+1,n-i-1 );  
  149.   
  150.     head->left = left;  
  151.     head->right = right;  
  152.     //返回頭節點   
  153.     return head;  
  154. }  
  155. //後+中恢復二叉樹   
  156. TREE* post_in(const int* post,const int* in, int n)  
  157. {  
  158.     if (n==0)  
  159.         return NULL;  
  160.   
  161.     TREE* head = new TREE();  
  162.     //後序與先序類似,最後一節點是根節點   
  163.     head->val = post[n-1];  
  164.     head->parent = NULL;  
  165.     //確定根在中序中的位置   
  166.     int i=0;  
  167.     for (;i<n;++i)  
  168.     {  
  169.         if( post[n-1] == in[i] )  
  170.             break;  
  171.     }  
  172.     //遞歸的構建左右子樹,這裏需要確定左/右子樹對應的後序/中序段   
  173.     TREE* left  = post_in(post,in,i);  
  174.     TREE* right = post_in(post+i,in+i+1,n-i-1);  
  175.   
  176.     head->left = left;  
  177.     head->right = right;  
  178.   
  179.     return head;  
  180. }  
  181.   
  182. //先+後恢復二叉樹   
  183. TREE* pre_post(const int* pre,const int* post, int n)  
  184. {  
  185.     if (n==0)  
  186.         return NULL;  
  187.   
  188.     TREE* head = new TREE();  
  189.   
  190.     head->val = pre[0];  
  191.     head->parent = NULL;  
  192.       
  193.     //對有唯一解的二叉樹,若將先序的第一個節點當做父節點,則第二個節點pre[1]必是左子樹節點   
  194.     //pre[1]在後序中的位置確定了pre[0](post[n-1])的左右子樹範圍   
  195.     if( n < 2 )  
  196.         return head;  
  197.   
  198.     int i = 0;    
  199.     for (;i<n-1;++i)  
  200.     {  
  201.         if( pre[1] == post[i] )  
  202.             break;  
  203.     }  
  204.   
  205.     TREE* left  = pre_post(pre+1,post,i+1);  
  206.     TREE* right = pre_post(pre+i+2,post+i+1,n-i-2);  
  207.   
  208.     head->left = left;  
  209.     head->right = right;  
  210.   
  211.     return head;  
  212. }  
  213.   
  214. int _tmain(int argc,char* argv[])  
  215. {  
  216.     TREE* pTree = new TREE();  
  217.   
  218.     //任意二叉樹,不能用於測試pre_post函數   
  219.     //const int N = 7;   
  220.     //int a[N] = {1,2,3,4,5,6,11};   
  221.       
  222.     //先+後有唯一解的二叉樹,用於測試pre_post函數   
  223.     const int N = 9;  
  224.     int a[N] = {1,2,3,4,5,6,7,10,11};  
  225.   
  226.     CreateTree(&pTree,a,N);  
  227.   
  228.   
  229.     int pre[N];  
  230.     int in[N];  
  231.     int post[N];  
  232.       
  233.     int* pre_ptr  = (int*)pre;  
  234.     int* in_ptr   = (int*)in;  
  235.     int* post_ptr = (int*)post;  
  236.   
  237.     PreOrder(pTree,&pre_ptr);  
  238.     InOrder(pTree,&in_ptr);  
  239.     PostOrder(pTree,&post_ptr);  
  240.   
  241.     cout<<"pre="<<endl;  
  242.     show(pre,N);  
  243.     cout<<"in="<<endl;  
  244.     show(in,N);  
  245.     cout<<"post="<<endl;  
  246.     show(post,N);  
  247.   
  248.     //------------- pre_in_post   
  249.     TREE* head = pre_in(pre,in,N);  
  250.     int pre_in_post[N];  
  251.     int* pre_in_post_ptr = (int*)pre_in_post;  
  252.     PostOrder(head,&pre_in_post_ptr);  
  253.     cout<<endl<<"pre_in_post:"<<endl;  
  254.     show(pre_in_post,N);  
  255.       
  256.     //------------- post_in_pre   
  257.     head = post_in(post,in,N);  
  258.     int post_in_pre[N];  
  259.     int* post_in_pre_ptr = (int*)post_in_pre;  
  260.     PreOrder(head,&post_in_pre_ptr);  
  261.     cout<<endl<<"post_in_pre:"<<endl;  
  262.     show(post_in_pre,N);  
  263.   
  264.     //------------- pre_post_in   
  265.     //注意:這種情況況不是任意二叉樹都有唯一解,只對這種二叉樹纔有唯一解:每個非葉節點都有個孩子   
  266.     head = pre_post(pre,post,N);  
  267.     int pre_post_in[N];  
  268.     int* pre_post_in_ptr = (int*)pre_post_in;  
  269.     InOrder(head,&pre_post_in_ptr);  
  270.     cout<<endl<<"pre_post_in:"<<endl;  
  271.     show(pre_post_in,N);  
  272.     return 0;  
  273. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章