利用後序和先序遍歷恢復二叉樹
阿新 • • 發佈:2018-11-27
利用後序和先序遍歷恢復二叉樹
利用後序和中序遍歷可以將二叉樹還原出來,以便於進行其他樹的操作。在這裡我們還原出二叉樹之後進行先序遍歷來求得先序遍歷的結果,我們約定還原樹的函式叫做RestoreTree()。
過程
後序遍歷例項:C B E H G I F D A
中序遍歷例項:B C A E D G H F I
中序遍歷開始位置,結束位置記做z1,z2,後序的記為h1,h2
- 新建一顆空樹,左右孩子置空
- 拿到後序遍歷的最後一個結點,其位置為z2,將該值存入樹的資料域
- 在中序遍歷的序列中以遍歷的方式找到最後一個結點的位置,記為i
- 如果i!=z1,說明以該結點為根結點的樹有左子樹,以遞迴的方式,呼叫當前函式恢復左子樹
- 如果i!=z2,說明以該結點為根結點的樹有右子樹,,以遞迴的方式呼叫當前函式恢復右子樹
- 返回樹的根結點值
需要注意的地方
在恢復左右子樹的時候,其位置需要算出來,即h1,h2和z1,z2的值需要重新計算,並在更新之後傳遞給RestoreTree()函式。以構建左子樹為例,左子樹的首元素下標為z1,最後一個元素下標為i-1,對應的h1的值為h1,h2的值為h1+(i-z1-1),也就是h1當前的位置向前移動i-z1-1個長度。
程式碼實現
以實現之前提到的字母序列為例
#include<stdio.h> #include<stdlib.h> typedef struct Bitree{ char data; struct Bitree* lchild; struct Bitree* rchild; }*bitree,bitnode; bitree Createtree(void); bitree Restoretree(int h1,int h2,int z1,int z2); void Preorder(bitree bt); char in[50],post[50]; int main(void) { int n; scanf("%d",&n); getchar(); //getchar()用於清除回車 for(int i = 0;i < n;i++) scanf("%c",&post[i]); getchar(); for(int i = 0;i < n;i++) scanf("%c",&in[i]); bitree b = Restoretree(0,n-1,0,n-1); Preorder(b); printf("\n"); return 0; } bitree Createtree(void) { bitree bt = (bitree)malloc(sizeof(bitnode)); bt->lchild = bt->rchild = NULL; return bt; } bitree Restoretree(int h1,int h2,int z1,int z2) { bitree bt = Createtree(); bt->data = post[h2]; for(int i = z1;i <= z2;i++) { if(in[i]==post[h2]) { if(i!=z1)//存在左子樹,構建左子樹 bt->lchild = Restoretree(h1,h1+i-z1-1,z1,i-1); if(i!=z2)//存在右子樹,構建右子樹 bt->rchild = Restoretree(h2-z2+i,h2-1,i+1,z2); break;//找到以後跳出遍歷 } } return bt; } void Preorder(bitree bt) { if(!bt) return; printf("%c ",bt->data); Preorder(bt->lchild); Preorder(bt->rchild); }
由於程式碼在恢復樹的時候先恢復的根結點,然後訪問的左右子樹,因此,恢復的過程也相當於一個先根遍歷的過程.如果只想求先根遍歷,可以不建樹.我們可以刪掉先根遍歷的函式,同時簡化一些其他的語句.修改後的程式碼如下:
#include<stdio.h> typedef struct Bitree{ char data; struct Bitree* lchild; struct Bitree* rchild; }*bitree,bitnode; void Restoretree(int h1,int h2,int z1,int z2); char in[50],post[50]; int main(void) { int n; scanf("%d",&n); getchar(); for(int i = 0;i < n;i++) scanf("%c",&post[i]); getchar(); for(int i = 0;i < n;i++) scanf("%c",&in[i]); Restoretree(0,n-1,0,n-1); printf("\n"); return 0; } void Restoretree(int h1,int h2,int z1,int z2) { printf("%c ",post[h2]); for(int i = z1;i <= z2;i++) { if(in[i]==post[h2]) { if(i!=z1)//存在左子樹,訪問左子樹 Restoretree(h1,h1+i-z1-1,z1,i-1); if(i!=z2)//存在右子樹,訪問右子樹 Restoretree(h2-z2+i,h2-1,i+1,z2); break;//找到以後跳出遍歷 } } }
兩段程式碼得到的結果是一樣的,以下是樣例輸入:
9
CBEHGIFDA
BCAEDGHFI
以下是輸出:
A B C D E F G H I
程式碼拓展
在這裡補充一段利用先序遍歷和中序遍歷來還原二叉樹並進行後序遍歷的程式碼。
#include<stdio.h>
#include<stdlib.h>
typedef struct BiTree{
int data;
struct BiTree* lchild;
struct BiTree* rchild;
}bitnode,*bitree;
void Postorder(bitree bt);
bitree RestoreTree(char* pre,char* in,int q1,int q2,int z1,int z2);
int main()
{
char pre[10] = {"DBACEGF"},in[10] = {"ABCDEFG"};
bitree bt= RestoreTree(pre,in,0,6,0,6);
Postorder(bt);
return 0;
}
bitree RestoreTree(char* pre,char* in,int q1,int q2,int z1,int z2)
{
bitree bt = (bitree)malloc(sizeof(bitnode));
bt->lchild = bt->rchild = NULL;
bt->data = pre[q1];
for(int i = z1;i<=z2;i++)
{
if(in[i]==pre[q1])
{
if(i!=z1)
bt->lchild = RestoreTree(pre,in,q1+1,q1+i-z1,z1,i-1);
if(i!=z2)
bt->rchild = RestoreTree(pre,in,q1+i-z1+1,z2,i+1,z2);
break;
}
}
return bt;
}
void Postorder(bitree bt)
{
if(bt->lchild!=NULL)
Postorder(bt->lchild);
if(bt->rchild)
Postorder(bt->rchild);
printf("%c",bt->data);
}
程式碼和之前一樣可以簡化,簡化以後,不需要建樹,也可以進行後序遍歷。
#include<stdio.h>
#include<stdlib.h>
typedef struct BiTree{
int data;
struct BiTree* lchild;
struct BiTree* rchild;
}bitnode,*bitree;
bitree RestoreTree(char* pre,char* in,int q1,int q2,int z1,int z2);
int main()
{
char pre[10] = {"DBACEGF"},in[10] = {"ABCDEFG"};
RestoreTree(pre,in,0,6,0,6);
return 0;
}
bitree RestoreTree(char* pre,char* in,int q1,int q2,int z1,int z2)
{
for(int i = z1;i<=z2;i++)
{
if(in[i]==pre[q1])
{
if(i!=z1)
RestoreTree(pre,in,q1+1,q1+i-z1,z1,i-1);
if(i!=z2)
RestoreTree(pre,in,q1+i-z1+1,z2,i+1,z2);
break;
}
}
printf("%c",pre[q1]);
}