0x01.問題
從前序與中序遍歷序列構造二叉樹
根據一棵樹的前序遍歷與中序遍歷構造二叉樹。
注意:
你可以假設樹中沒有重複的元素。
例如,給出:
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
0x02.算法詳細分析
我們知道,根據前序序列和中序序列是可以完全確定一棵二叉樹,那麼我們是怎麼根據前序序列和中序序列去確定這個這棵樹的呢?我們一起來探究一下。
1.第一個問題:我們要明白,前序序列和中序序列是怎麼得到的?
-
這應該是比較基礎的二叉樹知識了。
-
二叉樹的前序序列得到的方式:
- 遍歷根節點。
- 遞歸遍歷左子樹。
- 遞歸遍歷右子樹。
-
二叉樹的中序序列得到的方式:
- 遞歸遍歷左子樹。
- 遍歷根節點。
- 遞歸遍歷右子樹。
2.第二個問題:前序序列和中序序列中的值是怎樣對應原來樹中的結構的呢?
-
一定要將這個序列和原來的樹結合起來,知道序列中的值是如何分佈的,才能夠還原原來的那棵二叉樹。
-
根據上面得到序列的方式,我們應該不難得到,下面的這個關係:
-
也就是說,前序序列的根節點在最前面,然後是左子樹,然後是右子樹。
-
中序序列中先是前序序列,然後是根節點,然後是右子樹。
-
知道序列中是怎麼分佈後,我們似乎還沒有能夠構造出完整的樹的思路,因爲我們似乎遇到了一個困難:
- 假如我們第一個以前序序列的第一個節點構造了樹的根節點,下一個節點如何尋找?根節點的左右子樹到底在序列的哪個位置?
3.深度思考一下第三個問題:如何去序列中尋找對應的左右子樹?
-
既然根節點是前序序列的第一個節點,那麼根節點的左子樹如何尋找?
- 其實很簡單了,左子樹就是前序序列的第二個節點,因爲前序序列就是先遍歷左子樹,再遍歷右子樹,所以,左子樹就很好尋找了。
-
那麼右子樹如何去尋找呢?
- 我們最原始的想法很簡單,就是去找到這個位置就可以了。
- 我們最原始的想法很簡單,就是去找到這個位置就可以了。
-
因爲這個位置是右子樹的第一個節點,也就是根節點的右子樹。
-
那麼問題來了,中間這段距離怎麼求,也就是左子樹的數目。
-
不要想當然的認爲左子樹和右子樹的數量是相等的,因爲問題只說了是二叉樹,並沒有說是什麼特殊的二叉樹,所以並不能確定左子樹的數量。
-
這個時候,不要忘記,我們還有一箇中序序列,這個中序序列和前序序列所對應的二叉樹可是相同的,所以自然左子樹的數目也是相同的。
-
我們的想法是,要是知道根節點在中序序列中的位置,左子樹的數量就可以確定下來了。
-
而題目中說了,保證節點的值是唯一的,所以,只要一個個找,肯定是能找到的。
-
但是這樣查找就比較浪費時間,我們可以維護一個哈希表,每次去哈希表中查找,就能把時間的複雜度降下來。
4.具體的算法思路?
-
在上面的分析中,我們確定了基本的思路,也就是依次尋找根節點,左子樹,右子樹。
-
而在樹中,這個過程肯定是可以抽取出來的,也就是可以使用遞歸的方式將這個過程表示出來。
-
細節: 每一次遞歸需要哪些參數呢?
- 由於需要確定序列中左右子樹的邊界,所以對於每種序列來說,左右邊界當然是需要的。
-
細節:初始化?
- 初始化條件是
0,n-1
。
- 初始化條件是
-
細節:遞歸退出的條件?
- 當左邊界大於右邊界時,遞歸退出。
0x03.算法–從前序與中序遍歷序列構造二叉樹
class Solution {
private Map<Integer,Integer> indexMap;
private TreeNode toBuildTree(int[] preorder,int[] inorder,int pre_left,int pre_right,int in_left,int in_right){
//遞歸終止條件
if(pre_left>pre_right){
return null;
}
//建立根節點
TreeNode root=new TreeNode(preorder[pre_left]);
//得到中序序列中根節點的下標
int in_root=indexMap.get(preorder[pre_left]);
//得到前序序列中左子樹的長度
int size_left=in_root-in_left;
//遞歸建立左子樹
root.left=toBuildTree(preorder,inorder,pre_left+1,pre_left+size_left,in_left,in_root-1);
//遞歸建立右子樹
root.right=toBuildTree(preorder,inorder,pre_left+size_left+1,pre_right,in_root+1,in_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n=preorder.length;
if(n!=inorder.length){
return null;
}
indexMap=new HashMap<>();
for(int i=0;i<n;i++){
indexMap.put(inorder[i],i);
}
return toBuildTree(preorder,inorder,0,n-1,0,n-1);
}
}