整理自劍指Offer和牛客網的討論 https://www.nowcoder.com/questionTerminal/8b3b95850edb4115918ecebdf1b4d222
一:題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
二:解題思路
在二叉樹的前序遍歷中,第一個數總數根節點的值。但在中序遍歷中,根節點的值在序列的中間,左子樹的結點的值位於根節點的的值得左邊,而右子樹的結點的值位於根節點的右邊,因此我們需要掃描中序遍歷序列,才能找到根節點的值。
以例子說明:
如圖所示,前序遍歷第一個數字1就是根節點的值。掃描中序遍歷序列,就能確定根節點值得位置。根據中序遍歷的特點,在根節點的值1前面的3個數字都是左子樹結點的值,位於1後面的數字都是右子樹結點的值。
由於在中序遍歷中有3個數字都是左子樹結點的值,因此左子樹總共有3個左子結點。同樣在前序遍歷的序列中,根節點後面的3個數字就是3個左子樹結點的值。再後面的所有數字都是右子樹結點的值。這樣我們就在前序遍歷和中序遍歷中,分別找到了左右子樹對應的子序列。
接下來就可以在左子樹對應的先序與中序序列中遞歸調用函數找到當前節點的左結點,右子樹對應的先序和中序序列中遞歸調用函數找到當前節點的有結點
總結方法的步驟(遞歸):
1.前序遍歷序列第一個值是根節點,創建根節點
2.在中序遍歷序列中找到根節點位置(根據中序遍歷中根節點的位置,可以獲得左右子樹的大小)
3.計算左子樹前序遍歷序列的起始終止位置,中序遍歷序列的起始終止位置
4.計算右子樹前序遍歷序列的啓示終止位置,中序遍歷序列的起始終止位置
例如
先序遍歷序列pre
起始位置startPre
終止位置endPre
中序遍歷序列vin
起始位置startVin
終止位置endVin
根節點位於中序遍歷序列第i個位置,則左子樹的長度爲(i-startVin),右子樹長度(endVin-i)
所以:
左子樹:
先序遍歷 起始:startPre+1 終止:startPre+i-startVin
中序遍歷 起始:startVin 終止:i-1
右子樹:
先序遍歷 起始:startPre+i-startVin+1(左子樹終止+1) 終止 endPre
中序遍歷 起始:i+1 終止 endVin
三:代碼實現
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* reConstructBinaryTreeCore(vector<int> pre,int startPre,int endPre,vector<int> vin,int startVin,int endVin){
if(startPre>endPre || startVin>endVin)
return NULL;
//創建當前根結點
TreeNode* root=new TreeNode(pre[startPre]);
//在中序遍歷中找到根結點
for(int i=startVin;i<=endVin;i++)
if(vin[i]==pre[startPre]){
//重新計算左子樹pre-startPre,endPre vin--startVin.endVin
root->left=reConstructBinaryTreeCore(pre,startPre+1,startPre+i-startVin,vin,startVin,i-1);
//重新計算右子樹pre-startPre,endPre vin--startVin.endVin
root->right=reConstructBinaryTreeCore(pre,i-startVin+startPre+1,endPre,vin,i+1,endVin);
//這種方法很巧妙,不用定義額外的變量存放左右子樹中序,先序的信息
}
return root;
}
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
if(pre.size()==0 || vin.size()==0)
return NULL ;
TreeNode* root=reConstructBinaryTreeCore(pre,0,pre.size()-1,vin,0,vin.size()-1);
return root;
}
};