根據遍歷序列還原二叉樹
首先看一道PTA上的題目:
7-1 根據後序和中序遍歷輸出先序遍歷 (25 分)
本題要求根據給定的一棵二叉樹的後序遍歷和中序遍歷結果,輸出該樹的先序遍歷結果。
輸入格式:
第一行給出正整數N(≤30),是樹中結點的個數。隨後兩行,每行給出N個整數,分別對應後序遍歷和中序遍歷結果,數字間以空格分隔。題目保證輸入正確對應一棵二叉樹。
輸出格式:
在一行中輸出Preorder:
以及該樹的先序遍歷結果。數字間有1個空格,行末不得有多餘空格。
輸入樣例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
輸出樣例:
Preorder: 4 1 3 2 6 5 7
已知後序遍歷和中序遍歷,如何求出二叉樹呢?我們知道,後序遍歷的順序是“左、右、根”,因此,最後一個“4”必然是樹根。在中序序列中找到這個“4”,它將中序序列分成兩部分:“123”和“567”。於是我們就知道,這棵樹的左子樹含有1、2、3三個元素,右子樹含有5、6、7三個元素。即:
1 2 3 4 5 6 7
然後,我們看後序序列倒數第二個元素“6”,它必然是樹的右子樹的根節點。再到中序序列中找到6,它將右子樹分成“5”和“7”兩個部分,而後序序列倒數第三個元素為7,則7是右子樹的右子樹的根節點……以此類推,就可以將二叉樹還原。
但是,人用這種方法來做題是可以的,用程式碼卻不太容易實現,以下方法更適合程式碼實現:
首先找到根節點4,它將中序序列分成左(123)右(567)子樹,然後,我們對比觀察後序序列和中序序列:
2 3 1 5 7 6 4
1 2 3 4 5 6 7
不難發現規律:如果左子樹有m個元素,右子樹有n個元素,則後序序列中,前m個元素恰好對應左子樹,除去這m個元素以及根節點(最後一個元素),剩下的元素恰好與右子樹對應。
於是問題就遞迴轉化為:
已知後序序列為 2 3 1,中序序列為1 2 3,求這棵樹(即原樹的左子樹);
已知後序序列為 5 7 6,中序序列為5 6 7,求這棵樹(即原樹的右子樹)。
同樣的,已知前序序列和中序序列也可用類似方法解決。
//標頭檔案包含 #include<stdlib.h> #include<stdio.h> #include<malloc.h> //函式狀態碼定義 #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -1 #define INFEASIBLE -2 typedef int Status; //二叉連結串列儲存結構定義 typedef int TElemType; typedef struct BiTNode{ TElemType data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; BiTree CreateBiTree(int *in,int *post, int n) { if(n<=0) return NULL; int *p = in; while(p) { if(*p==*(post+n-1)) break; p++; } BiTree T = (BiTree)malloc(sizeof(BiTNode)); T->data = *p; int len = p-in; T->lchild = CreateBiTree(in,post,len); T->rchild = CreateBiTree(p+1,post+len,n-len-1); return T; } Status PreOrderTraverse(BiTree T) { if(!T) return OK; else { printf(" %d",T->data); PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } return OK; } int main() { int in[35],post[35]; int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%d",&post[i]); for(int i=0;i<n;i++) scanf("%d",&in[i]); BiTree T; T = CreateBiTree(in,post,n); printf("Preorder:"); PreOrderTraverse(T); return 0; }