引言
這兩天偶然看到一道題,應該是《劍指offer》上的,原題如下:
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
解題思路
在解題之前我們需要知道前序,中序,後序是怎麼遍歷的,這裏不多做介紹。我們將前序遍歷序列設爲pre,中序遍歷序列設爲vin。
1. 首先我們需要確定根節點,很簡單,就是前序遍歷的第一個元素pre[0]=1;
2. 因爲對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊
這樣我們就把整個序列分爲了兩部分,{4,7,2}在根節點的左邊,{5,3,8,6}在根節點的右邊
3. 針對{4,7,2},{5,3,8,6}分別重複執行第二步,直到所有節點都分配完畢,具體步驟如下:
3.1 對於{4,7,2},查看前序遍歷序列{1,2,4,7,3,5,6,8},發現1後面的節點爲2,也就是說2爲{4,7,2}這個左分支的根節點;
注意上圖中此時沒有右樹
3.2 依次類推,直接上圖;
3.3 對於{5,3,8,6}重建的方法和左樹一樣,直接上圖;
3.4 每一步其實就是利用前序遍歷序列找到根節點,再利用中序遍歷序列將左右子樹分開,最後再合併起來;
代碼
將解題思路總結爲代碼,如下:
TreeNode* reConstructBinaryTree(vector<int> pre,vector<int> vin)
{
int inlen=vin.size();
if(pre.empty() || vin.empty()) return NULL;
else if(inlen != pre.size()) return NULL;
vector<int> left_pre,right_pre,left_in,right_in;
//創建根節點,根節點爲前序遍歷的第一個元素
TreeNode* head=new TreeNode(pre[0]);
//找到中序遍歷根節點所在位置,存放於變量gen中
int gen=0;
for(int i=0;i<inlen;i++)
{
if (vin[i]==pre[0])
{
gen=i;
break;
}
}
//對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊
for(int i=0;i<gen;i++) //找到左樹
{
left_in.push_back(vin[i]);
left_pre.push_back(pre[i+1]);
}
for(int i=gen+1;i<inlen;i++) //找到右樹
{
right_in.push_back(vin[i]);
right_pre.push_back(pre[i]);
}
//遞歸,劃分子樹的左、右樹,直到葉節點
head->left=reConstructBinaryTree(left_pre,left_in);
head->right=reConstructBinaryTree(right_pre,right_in);
return head;
}
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
return root;
}
//前序遍歷{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}
private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
if(startPre>endPre||startIn>endIn)
return null;
TreeNode root=new TreeNode(pre[startPre]);
for(int i=startIn;i<=endIn;i++)
if(in[i]==pre[startPre]){
root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1);
root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn);
}
return root;
}
}