問題
已知一棵二叉樹的先序遍歷以及中序遍歷,重建二叉樹。二叉樹的每一個節點有三個屬性,左子節點,右子節點,以及節點值。
思路
先序遍歷服從規則“根左右”,所以由此可知,對於一個先序遍歷得到的數組,第一個元素一定是根節點;
中序遍歷服從規則”左根右“,所以由此可知,對於一個中序遍歷得到的數組,根節點左邊的元素都屬於根節點的左子樹,而根節點右邊的元素都屬於根節點的右子樹;
所以,我們可以先通過先序遍歷的第一個元素確定根節點,然后通過中序遍歷結合根節點,獲得當前根節點的左右子樹,再將子樹看成一棵獨立的樹,繼續使用先序遍歷判斷根節點,中序遍歷判斷子樹的方式,最終建立起整棵樹;
例子
假設有一棵二叉樹,先序遍歷為{1,2,4,7,3,5,6,8},中序遍歷為{4,7,2,1,5,3,8,6},則建樹過程如下:
首先,通過先序遍歷可知樹的根節點為1,則在中序遍歷中,1左邊的元素4,7,2即為根的左子樹的元素,而1右邊的元素5,3,8,6即為根節點的右子樹;
對於左子樹4,7,2來說,在先序遍歷中,這三個點的順序為2,4,7,則2為根節點,而在中序遍歷中,4,7均在2的左邊,則4,7均為以2為根樹的左子樹,且沒有右子樹;
對於4,7這兩個節點來說,先序遍歷中,4節點在7節點之前,所以4為根節點,而7作為子樹,在中序遍歷中,7在4之后,所以7為右子樹;
對於根節點1的右子樹5,3,8,6來說,在先序遍歷中,3在最前面,所以3為這棵子樹的根節點,而在中序遍歷中,5在3的左邊,所以屬於左子樹,而8,6在3的右邊,屬於右子樹;
對於根節點3的右子樹8,6,在先序遍歷中,6在8之前,所以,6又為根節點,而在中序遍歷中,8在6的左邊,所以8是6的左子節點;
至此,二叉樹便重建完成;
代碼
樹的節點
public class TreeNode {
int val; //當前節點的值
TreeNode left; //左子節點
TreeNode right; //右子節點
TreeNode(int x) {
val = x;
}
}
建樹方法
/**
* pre:線序遍歷得到的數組
* in:中序遍歷得到的數組
*/
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
if(pre.length == 0) {
return null;
}
int root = pre[0];
TreeNode node = new TreeNode(root);
//尋找根節點在in中的索引
int i = 0;
for( ; i<in.length; ++i) {
if(in[i] == root) {
break;
}
}
//建立左子樹
int[] leftIn = Arrays.copyOfRange(in, 0, i);
int[] leftPre = Arrays.copyOfRange(pre, 1, i+1);
node.left = reConstructBinaryTree(leftPre, leftIn);
//建立右子樹
int[] rightIn = Arrays.copyOfRange(in, i+1, in.length);
int[] rightPre = Arrays.copyOfRange(pre, i+1, pre.length);
node.right = reConstructBinaryTree(rightPre, rightIn);
return node;
}
建樹代碼(優化)
public TreeNode reConstructBinaryTree(int[] pre, int[] in) {
return getRootTreeNode(pre, 0, pre.length-1, in, 0, in.length-1);
}
/**
* preL:當前子樹在先序遍歷的數組中的起始下標
* preR:當前子樹在先序遍歷的數組中的結束下標
* inL:當前子樹在中序遍歷的數組中的起始下標
* inR:當前子樹在中序遍歷的數組中的起始下標
*/
public TreeNode getRootTreeNode(int[] pre, int preL,
int preR, int[] in, int inL, int inR) {
if(preL > preR) {
return null;
}
TreeNode node = new TreeNode(pre[preL]);
for(int i=inL; i<=inR; ++i) {
if(in[i] == pre[preL]) {
node.left = getRootTreeNode(pre, preL+1, preL+i-inL, in, inL, i-1);
node.right = getRootTreeNode(pre, preL+i-inL+1, preR, in, i+1, inR);
break;
}
}
return node;
}