樹的遍歷
給定一棵二叉樹的後序遍歷和中序遍歷,請你輸出其層序遍歷的序列。這裏假設鍵值都是互不相等的正整數。
輸入格式:
輸入第一行給出一個正整數N(≤30),是二叉樹中結點的個數。第二行給出其後序遍歷序列。第三行給出其中序遍歷序列。數字間以空格分隔。
輸出格式:
在一行中輸出該樹的層序遍歷的序列。數字間以1個空格分隔,行首尾不得有多餘空格。
輸入樣例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
輸出樣例:
4 1 6 3 5 7 2
解題思路:開兩個全局數組存放中序/後序遍歷輸入。然後遞歸建樹+層序遍歷
要點:
1.遞歸建樹判斷左右子樹是否存在,若存在如何遞歸,若不存在則子結點賦爲NULL
2.層序遍歷藉助隊列
建樹代碼:
TreeNode* build(int inB,int inE,int postB,int postE){
//建樹 inB、inE分別是中序遍歷數組的首尾元素索引;postB、postE則是後序的
int i = 0;
TreeNode* newnode =new TreeNode;
newnode->val = postor[postE];//後序最後一個輸入即根節點
while(inorder[inB + i] != postor[postE])//找到根節點
i++;
if(i > 0)//左子樹存在
newnode->lchild = build(inB,inB + i - 1,postB,postB + i -1);//中後左子樹長度相等,且位置相同
else
newnode->lchild = NULL;//即使是空值也必須初始化!
if(inB + i < inE)//右子樹存在
newnode->rchild = build(inB + i + 1,inE,postB + i ,postE - 1);//注意最後一個結點是已經賦給樹的根節點,應該刨除
else
newnode->rchild = NULL;//同上,否則層序遍歷無法進行
return newnode;
}//build
這裏說一下左右子樹的存在判定條件以及左右子樹如何實現遞歸建立:
判定條件:
中序遍歷中,根節點的左邊表示左子樹。如果根節點左邊還有值,說明左子樹存在。i是根節點到中序遍歷第一個值(最左邊一個值)的距離,當距離i > 0說明中序遍歷中當前節點的值並不位於最左邊,它的左邊還有值,即左子樹存在。
同理,中序中根節點的右側表示右子樹。若根節點(inB + i)右邊還有值說明右子樹存在。是否還有值通過根節點的索引到最後一個數的索引之間是否相等來判斷。相等則沒有值,小於則還有值(inB+i<inE) 即右子樹存在。
遞歸建立:
如何遞歸應該是這題的一個難點。
建立左子樹:
先考慮中序遍歷的兩個參數 inB 和 inE:每次遞歸取根節點(根節點索引爲inB + i)的左半部分,也就是[inB,inB + i -1]
然後後序遍歷的兩個參數postB及postE:每次遞歸取根節點的左子樹部分。那麼後序遍歷中某個特定的根節點左子樹應該怎麼確定呢?實際上,這裏左子樹的元素數跟上面中序的左半部分元素數應該是相等的(因爲是同一個根節點的子節點,二者表述的意義相同只是方式順序不同)並且我們知道,中後序都是先輸出左子樹。也就是說,只要在後序遍歷上截出一個跟中序左子樹同樣長度的序列(inB+i-1-inB = i-1),就是我們要的部分。這個部分用代碼描述:[postB,postB + i - 1]
建立右子樹
同樣先考慮中序部分:取根節點的右半部分區間 [inB + i + 1,inE]
然後後序遍歷的兩個參數:由上得,後序遍歷的前半部分是左子樹[postB,postB + i - 1],而我們知道最後一個元素是根節點(postE)。那麼剩下的部分就是右子樹。即 [postB +i ,postE - 1] (後序遍歷的數組各個部分在二叉樹中的分佈參照下圖:)
可以對比着看看 中序遍歷數組結構如下
層序遍歷代碼:
void levelorder(TreeNode* T){
//藉助隊列,層次遍歷
queue<TreeNode*> q;
if(T == NULL)
exit(-1);
q.push(T);
TreeNode* front;
while(!q.empty()){
front = q.front();
q.pop();
if(front->lchild != NULL){//左結點入隊
q.push(front->lchild);
}
if(front->rchild != NULL)//右結點入隊
q.push(front->rchild);
if(front->val != T->val)//輸出
cout<<" ";
cout<<front->val;
}
} //levelorder
層序遍歷這裏主要就是藉助隊列來完成。詳細可以看看這篇博文:https://blog.csdn.net/monster_ii/article/details/82115772
與上文不同的是,本題要求輸出末尾不可以有空格。所以空格乾脆在每一個數據之前輸出,然後特判一下第一個數據(根節點)不輸出即可。
完整的AC代碼:
#include<bits/stdc++.h>
using namespace std;
typedef struct TreeNode{
int val;
struct TreeNode *lchild,*rchild;
}TreeNode;
int inorder[35],postor[35];//因爲函數build需要用到,中序後序應該建成全局數組
TreeNode* build(int inB,int inE,int postB,int postE){
//建樹
int i = 0;
TreeNode* newnode =new TreeNode;
newnode->val = postor[postE];//後序最後一個輸入即根節點
while(inorder[inB + i] != postor[postE])//找到根節點
i++;
if(i > 0)//左子樹存在
newnode->lchild = build(inB,inB + i - 1,postB,postB + i -1);//中後左子樹長度相等,且位置相同
else
newnode->lchild = NULL;//即使是空值也必須初始化!
if(inB + i < inE)//右子樹存在
newnode->rchild = build(inB + i + 1,inE,postB + i ,postE - 1);//注意最後一個結點是已經賦給樹的根節點,應該刨除
else
newnode->rchild = NULL;//同上,否則層序遍歷無法進行
return newnode;
}//build
void levelorder(TreeNode* T){
//藉助隊列,層次遍歷
queue<TreeNode*> q;
if(T == NULL)
exit(-1);
q.push(T);
TreeNode* front;
while(!q.empty()){
front = q.front();
q.pop();
if(front->lchild != NULL){//左結點入隊
q.push(front->lchild);
}
if(front->rchild != NULL)//右結點入隊
q.push(front->rchild);
if(front->val != T->val)//輸出
cout<<" ";
cout<<front->val;
}
} //levelorder
void DeleteTree(TreeNode * T)
{
if(T == NULL) return;
DeleteTree(T->lchild);
DeleteTree(T->rchild);
delete T;
}
int main(){
int n,i;
cin>>n;
for(i = 1;i <= n; i++)//輸入後序遍歷
cin>>postor[i];
for(i = 1;i <= n; i++)//輸入中序遍歷
cin>>inorder[i];
TreeNode *T = build(1,n,1,n);//建樹
levelorder(T);//層序遍歷
DeleteTree(T);
return 0;
}
建樹部分參考自:https://blog.csdn.net/weixin_40163242/article/details/88579989
注意不可直接結合兩個博文,應該在第二個連接的基礎上加上這個部分:
if(i > 0)//左子樹存在
newnode->lchild = build(inB,inB + i - 1,postB,postB + i -1);//中後左子樹長度相等,且位置相同
else
newnode->lchild = NULL;//即使是空值也必須初始化!
if(inB + i < inE)//右子樹存在
newnode->rchild = build(inB + i + 1,inE,postB + i ,postE - 1);//注意最後一個結點是已經賦給樹的根節點,應該刨除
else
newnode->rchild = NULL;//同上,否則層序遍歷無法進行
因爲第二篇博文使用的層序遍歷稍微複雜一些(使用了vector),好像不需要左右子節點爲空的時候置NULL(這個bug老是段溢出找了我三個點。。。我太難了┭┮﹏┭┮)