1. 題目描述
輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。
2. 解題思路
在二叉樹的前序遍歷中,第一個數字總是樹的根節點。在中序遍歷中,樹的根節點在序列的中間某個位置,左子樹的節點的值位於根節點的左邊,右子樹節點的值位於根節點值的右邊。
在找到了整顆樹的根節點後,掃描中序遍歷序列找到第一個根結點的位置,由此可以找到左子樹的節點的個數和右子樹節點的個數,然後在前序遍歷序列中找到左子樹的根節點,再到中序遍歷序列中找到左子樹的左子樹和右子樹。依次遞歸。
由於二叉樹的構造本身就是用遞歸實現的,所以重建二叉樹也用遞歸進行實現實很簡單的。
則重建出下圖的二叉樹
1
/ \
2 3
/ / \
4 5 6
\ /
7 8
3. 代碼實現
版本一:
public class RebuildingBinaryTree {
public static void main(String[] args) {
int[] preOrder={1,2,4,7,3,5,6,8}; //前序遍歷序列
int[] inOrder={4,7,2,1,5,3,8,6}; // 中序遍歷序列
BinaryTreeNode node=reConstruct(preOrder,inOrder);
printTree(node);
}
//二叉樹節點(靜態內部類)
public static class BinaryTreeNode{
int value;
BinaryTreeNode left;
BinaryTreeNode right;
}
// 判斷前序和中序的輸入合法性
public static BinaryTreeNode reConstruct(int[] preOrder,int[] inOrder){
if(preOrder==null||inOrder==null||preOrder.length!=inOrder.length||preOrder.length<1){
return null;
}
return construct(preOrder,0,preOrder.length-1,inOrder,0,inOrder.length-1);
}
/**
* 根據前序遍歷和中序遍歷構建二叉樹
* @param preOrder 前序遍歷序列
* @param ps 前序遍歷開始位置
* @param pe 前序遍歷結束位置
* @param inOrder 中序遍歷序列
* @param is 中序遍歷開始位置
* @param ie 中序遍歷結束位置
* @return 構建的樹的根節點
*/
public static BinaryTreeNode construct(int[] preOrder,int ps,int pe,int[] inOrder,int is,int ie){
if(ps>pe) // 開始位置大於結束位置說明已經處理到葉節點了
return null;
int value=preOrder[ps]; // 前序遍歷第一個數字爲當前的根節點
int index=is;
while(index<=ie&&inOrder[index]!=value){ //index爲根節點在中序遍歷序列中的索引
index++;
}
System.out.println("當前各參數的數值爲->ps:"+ps+" pe:"+pe+" is:"+is+" ie:"+ie+" index:"+index+" rootValue:"+value);
if(index>ie){ //如果在整個中序遍歷中沒有找到根節點說明輸入的數據是不合法的
throw new RuntimeException("invalid input"+index);
}
BinaryTreeNode node=new BinaryTreeNode();
node.value=value;
//當前節點的左子樹的個數爲index-is
//左子樹對應的前序遍歷的位置在preOrder[ps+1,ps+index-is]
//左子樹對應的中序遍歷的位置在inOrder[is,index-1]
node.left=construct(preOrder,ps+1,ps+index-is,inOrder,is,index-1);
//當前節點的右子樹的個數爲ie-index
//右子樹對應的前序遍歷位置在preOrder[ps+index-is+1,pe]
//右子樹對應的中序遍歷位置在inOrder[index+1,ie]
node.right=construct(preOrder,ps+index-is+1,pe,inOrder,index+1,ie);
return node;
}
//中序遍歷遞歸打印
public static void printTree(BinaryTreeNode node){
if(node!=null){
printTree(node.left);
System.out.print(node.value+" ");
printTree(node.right);
}
}
}
版本二:
下面只是檢查條件沒有了,默認給出的前序遍歷和中序遍歷是正確的。
public class RebuildingBinaryTree {
public static void main(String[] args) {
int[] pre={1,2,4,7,3,5,6,8}; //前序遍歷序列
int[] in={4,7,2,1,5,3,8,6}; // 中序遍歷序列
RebuildingBinaryTree rsb=new RebuildingBinaryTree();
TreeNode nn=rsb.reConstructBinaryTree(pre,in);
System.out.println("............................");
rsb.printTree(nn);
}
//二叉樹節點(靜態內部類)
public class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode(int x){
val = x;
}
}
// 重載方法一
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1);
//System.out.println(root.val);
return root;
}
// 重載方法二:前序遍歷{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6}
public TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) {
System.out.println("當前各參數的數值爲->startPre:"+startPre+" endPre:"+endPre+" startIn:"+startIn+" endIn:"+endIn+"\n");
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]){
System.out.println(".."+"startPre:"+startPre+" i:"+i+" startIn:"+startIn+" root:"+root.val);
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);
break;
}
return root;
}
//中序遍歷遞歸打印
public void printTree(TreeNode node){
if(node!=null){
printTree(node.left);
System.out.print(node.val+" ");
printTree(node.right);
}
}
}
運行:
當前各參數的數值爲->startPre:0 endPre:7 startIn:0 endIn:7
..startPre:0 i:3 startIn:0 root:1
當前各參數的數值爲->startPre:1 endPre:3 startIn:0 endIn:2
..startPre:1 i:2 startIn:0 root:2
當前各參數的數值爲->startPre:2 endPre:3 startIn:0 endIn:1
..startPre:2 i:0 startIn:0 root:4
當前各參數的數值爲->startPre:3 endPre:2 startIn:0 endIn:-1
當前各參數的數值爲->startPre:3 endPre:3 startIn:1 endIn:1
..startPre:3 i:1 startIn:1 root:7
當前各參數的數值爲->startPre:4 endPre:3 startIn:1 endIn:0
當前各參數的數值爲->startPre:4 endPre:3 startIn:2 endIn:1
當前各參數的數值爲->startPre:4 endPre:3 startIn:3 endIn:2
當前各參數的數值爲->startPre:4 endPre:7 startIn:4 endIn:7
..startPre:4 i:5 startIn:4 root:3
當前各參數的數值爲->startPre:5 endPre:5 startIn:4 endIn:4
..startPre:5 i:4 startIn:4 root:5
當前各參數的數值爲->startPre:6 endPre:5 startIn:4 endIn:3
當前各參數的數值爲->startPre:6 endPre:5 startIn:5 endIn:4
當前各參數的數值爲->startPre:6 endPre:7 startIn:6 endIn:7
..startPre:6 i:7 startIn:6 root:6
當前各參數的數值爲->startPre:7 endPre:7 startIn:6 endIn:6
..startPre:7 i:6 startIn:6 root:8
當前各參數的數值爲->startPre:8 endPre:7 startIn:6 endIn:5
當前各參數的數值爲->startPre:8 endPre:7 startIn:7 endIn:6
當前各參數的數值爲->startPre:8 endPre:7 startIn:8 endIn:7
............................
4 7 2 1 5 3 8 6
以上是參數了各路大神代碼後的分析與總結。
參考: