(二叉樹)從中序和後序遍歷構造樹
題目描述
根據一棵樹的中序遍歷與後序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出
中序遍歷 inorder = [9,3,15,20,7]
後序遍歷 postorder = [9,15,7,20,3]
返回如下的二叉樹:
[3,9,20,null,null,15,7]
題目分析
對於樹,只要給定樹的中序遍歷+前序遍歷或者中序遍歷+後序遍歷結果,那麼就一定能夠構造出樹的結構。如何構造呢?我們知道,前序遍歷的第一個結點必定是根結點,後序遍歷的最後一個結點一定是根結點,根據前序遍歷或者後續遍歷很容易就能找到根結點,如果樹中不存在重複結點,那麼在中序遍歷中,根結點所在位置的左邊就是左子樹的中序遍歷結果,右邊就是右子樹的中序遍歷結果。什麼意思呢?以給出的例子 中序遍歷 inorder = [9,3,15,20,7]和後序遍歷 postorder = [9,15,7,20,3]來分析:
由後序遍歷可以知道根結點為3,再來到中序遍歷中,那麼3的左邊和右邊就分別是根結點的左子樹和右子樹的中序遍歷結果,即根結點的左子樹中序遍歷為[9],右子樹中序遍歷為[15,20,7],很顯然,根結點的左子樹就只剩一個結點9了。
現在再來分析右子樹:前面根據中序遍歷可以知道,根結點的左子樹上只有1個結點,右子樹上有3個結點,結合後序遍歷是左-右-根的特點,即後序遍歷中必定是先遍歷根結點的左子樹,然後再是右子樹,絕不會出現交叉的現象,因此在後序遍歷結果中從左往右數1個,即是左子樹的後序遍歷結果,再接著數3個結點,即是右子樹的後序遍歷結果,最後剩下一個根結點,由此可以分析出:根結點為3,對於左子樹:中序遍歷為[9],後序遍歷為[9];對於右子樹,中序遍歷為[15,20,7],後序遍歷為[15,7,20],這樣就可以分別得到左子樹和右子樹的中序和後序遍歷結果,再如同上面分析即可。
那麼現在來總結一下思路:
如圖所示,先從後序遍歷的末尾出發,找到中序遍歷中根結點所在位置pos,那麼instart ~ pos-1則是左子樹的中序遍歷,pos+1 ~ inend則是右子樹的中序遍歷,同樣,postart ~ postart+pos-instart-1則是左子樹的後序遍歷,postart+pos-instart ~ poend-1則是右子樹的後序遍歷,這樣,就最終找到左子樹和右子樹各自的的前序遍歷和後序遍歷,這樣一直遞迴下去,將instart和pos-1作為左子樹的instart和inend,pos+1和inend作為右子樹的instart和inend;postart 和 postart+pos-instart-1作為左子樹的postart和poend,postart+pos-instart和poend作為右子樹的postart和poend,可見,instart和inend、postart和poend是在不斷靠近的,直到instart>inend時,說明左子樹為空,此時postart必定大於poend,遞迴停止,寫出程式如下:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.empty())return NULL;
TreeNode * root=(TreeNode*)malloc(sizeof(TreeNode));
int len=inorder.size();
int pos=0;
root->val=postorder[len-1];
while(inorder[pos]!=root->val)pos++;
root->left=build(inorder,postorder,0,pos-1,0,pos-1);
root->right=build(inorder,postorder,pos+1,len-1,pos,len-2);
return root;
}
TreeNode* build(vector<int>& inorder, vector<int>& postorder,int instart,int inend,int postart,int poend)
{
if(instart>inend)return NULL;
TreeNode * root=(TreeNode*)malloc(sizeof(TreeNode));
root->val=postorder[poend];
int pos;
for(pos=instart;pos<=inend;pos++)
if(inorder[pos]==root->val)break;
root->left=build(inorder,postorder,instart,pos-1,postart,pos-1-instart+postart);
root->right=build(inorder,postorder,pos+1,inend,pos-instart+postart,poend-1);
return root;
}