題目:
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。
例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
3
/ \
9 20
/ \
15 7
限制:
0 <= 節點個數 <= 5000
思路:
前序遍歷:根左右(例如:【1, 2, 4,7, 3, 5, 6, 8】)
中序遍歷:左根右(對應:【4, 7, 2, 1, 5, 3, 8, 6】)
根據這個條件,我們先分析不使用代碼的時候我們如何還原一棵樹
①找到前序遍歷中當前第一個元素,將其作爲當前當前這棵樹的root結點
【1】
②在中序遍歷中找到這個節點,並將中序遍歷分成兩部分,左側就是這個root結點的左子樹內容,右側就是這個root結點的右子樹內容;(中序遍歷:【4, 7, 2, 1, 5, 3, 8, 6】)
左側:【4, 7, 2】; root:【1】;右側:【5, 3, 8, 6】;
③對左子樹和右子樹重複① 和 ②的過程;
很顯然這是一個遞歸調用的過程;
其中的關鍵:1. 遞歸結束的標誌是什麼? 2. 在遞歸調用過程中,入參需要傳入子樹的前序遍歷和中序遍歷/ 或是索引,這裏如何保證索引正確
首先:如果我們使用索引作爲標誌,那麼遞歸結束的標誌應該是當前傳入的子樹的開始索引與結束索引出現交叉,我們在設計遞歸傳入參數的時候,應該使得索引始終是合法的,這樣我們就只需要檢測是否出現交叉就ok了;
第二:我們應該計算出中序遍歷中我們選定的root結點左右兩側剩餘元素的個數,這樣在前序遍歷的數組中我們纔可以找到合適個數的元素分別傳入遞歸調用的左子樹函數和右子樹函數;
Code:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* constructTree(vector<int>& preorder,int p_start,int p_end ,vector<int>& inorder,int i_start,int i_end)
{
if(p_start <= p_end && i_start <= i_end && p_start >=0 && i_start >= 0){
TreeNode* root = new TreeNode(preorder[p_start]);
int i_left_remian = 0, i_right_remian = 0;
int i_root = 0;
for(int i = i_start ; i <= i_end; i++)
{
if(inorder[i] == root->val){
i_root = i;
}
}
i_left_remian = i_root - i_start; // 根節點左側剩下的元素個數(包含i_start)
i_right_remian = i_end-i_start-1; // 根節點右邊剩下元素個數
root->left = constructTree(preorder,p_start+1, p_start+i_left_remian , inorder, i_start, i_root-1);
root->right = constructTree(preorder,p_start+i_left_remian + 1, p_end , inorder, i_root+1, i_end);
return root;
}
else
return NULL;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.size() == 0 || (inorder.size() != preorder.size()))
return NULL;
int p_start = 0, p_end = preorder.size() -1;
int i_start = 0 ,i_end = inorder.size() -1;
TreeNode* root = constructTree(preorder,p_start, p_end , inorder, i_start, i_end);
return root;
}
};