1. 程式人生 > >重建二叉樹 及遍歷

重建二叉樹 及遍歷

題目描述

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回

劍指offer第63頁

//首先建立一個二叉樹類
/**
 * 二叉樹定義
 *  tree.left 左節點
 *  tree.right 右節點
 */
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
        val = x;
    }
}

//solution類,實現重建二叉樹
public class Solution {
    //主功能函式
    public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
        if(pre == null || in == null){
            return null;
        }
        TreeNode mm = reConstructBinaryTreeCore(pre, in, 0, pre.length-1, 0, in.length-1);
        return mm;
    }
    //核心遞迴
    public static TreeNode reConstructBinaryTreeCore(int[] pre, int[] in, int preStart, int preEnd, int inStart, int inEnd) {

        /**
         * 把前序遍歷的值賦給二叉樹的根節點
         *  tree.left 左節點
         *  tree.right 右節點
         */
        TreeNode tree = new TreeNode(pre[preStart]);
        tree.left = null;
        tree.right = null;
/**
 * preStart,preEnd為陣列的索引
 * pre[preStart],獲取該二叉樹的前序遍歷節點的值
 *
 */
        if (preStart == preEnd && inStart == inEnd) {//如果(preStart == preEnd,說明樹只有一個元素,該元素就是樹唯一的元素
            return tree;
        }
        int root = 0;

        //<editor-fold desc="對中序in[]陣列遍歷得到分支">
        /**
         * 由二叉樹的性質
         * 由前序遍歷的值pre_i來確定根節點
         * 遍歷中序遍歷的值,尋找中序遍歷中的根節點pre_j
         * 當中序遍歷的值in_j==pre_i時,可以確定該根節點的左右子樹
         */
        //</editor-fold>
        for(root= inStart; root < inEnd; root++){
            if (pre[preStart] == in[root]) {
                break;
            }
        }

        //<editor-fold desc="根節點為tree,對左右分支分別使用前序和中序遍歷進行描述並分組,對分組使用核心遞迴方法,得到tree.left
        // 和tree.right
        // 然後依次得到tree.left的左右節點....">
        /**
         * 得到中序遍歷的節點的值
         * leifLength 左子樹的元素個數(不含根節點)
         * rightLength 右子樹的元素個數(不含根節點)
         */
        //</editor-fold>
        int leifLength = root - inStart;
        int rightLength = inEnd - root;
        if (leifLength > 0) {
            //得到左節點的值
            tree.left = reConstructBinaryTreeCore(pre, in, preStart+1, preStart+leifLength, inStart, root-1);
        }
        if (rightLength > 0) {
            //得到右節點的值
            //下一層遞迴,則會得到tree.right的左或右節點......
            tree.right = reConstructBinaryTreeCore(pre, in, preStart+1+leifLength, preEnd, root+1, inEnd);
        }
        return tree;
    }


    //<editor-fold desc="遍歷測試">
   /* //將二叉樹先序遍歷,用於測試結果
    public static void preTraverseBinTree(TreeNode node){
        if (node==null) {
            return;
        }
        System.out.print(node.val+",");
        if (node.left!=null) {
            preTraverseBinTree(node.left);
        }
        if(node.right!=null){
            preTraverseBinTree(node.right);
        }
    }
    //將二叉樹中序遍歷,用於測試結果
    public static void inTraverseBinTree(TreeNode node){
        if (node==null) {
            return;
        }
        if (node.left!=null) {
            inTraverseBinTree(node.left);
        }
        System.out.print(node.val+",");
        if(node.right!=null){
            inTraverseBinTree(node.right);
        }
    }
    */
    //</editor-fold>

    //將二叉樹後序遍歷,用於測試結果
    public static void postTraverseBinTree(TreeNode node){
        if (node==null) {
            return;
        }
        if (node.left!=null) {
            postTraverseBinTree(node.left);
        }
        if(node.right!=null){
            postTraverseBinTree(node.right);
        }
        System.out.print(node.val+",");
    }

    //主函式,用於測試結果
    public static void main(String[] args){
        int pre[] = {1,2,4,7,3,5,8,9,6};
        int in[]  = {4,7,2,1,8,5,9,3,6};
        TreeNode tree = reConstructBinaryTree(pre, in);

        //<editor-fold desc="測試">
       /* System.out.print("先序遍歷結果:  {");
        preTraverseBinTree(tree);
        System.out.println("}");
        System.out.print("中序遍歷結果:  {");
        inTraverseBinTree(tree);
        System.out.println("}");*/
        //</editor-fold>

        System.out.print("後序遍歷結果:  {");
        postTraverseBinTree(tree);
        System.out.println("}");
    }
}

程式碼較長,可以先簡要分析一下:

1)建立一個二叉樹類

//首先建立一個二叉樹類
/**
 * 二叉樹定義
 *  tree.left 左節點
 *  tree.right 右節點
 */
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) {
        val = x;
    }
}

 思路:

a、根據前序遍歷的陣列第一個元素為根節點pre_i,在中序遍歷陣列中找到元素in_j=pre_i;設tree=pre_i

       for(root= inStart; root < inEnd; root++){
            if (pre[preStart] == in[root]) {
                break;
            }
        }

b、由中序遍歷的思想,pre_i為根節點,可以將二叉樹分為左右子樹,所以pre_j左邊的元素為左子樹中的元素,集合為{L},pre_j右邊的元素為右子樹中的元素,集合為{R}(此時具體組合未知);

     int leifLength = root - inStart;
        int rightLength = inEnd - root;
        if (leifLength > 0) {
            //得到左節點的值
            tree.left = reConstructBinaryTreeCore(pre, in, preStart+1, preStart+leifLength, inStart, root-1);
        }
        if (rightLength > 0) {
            //得到右節點的值
            //下一層遞迴,則會得到tree.right的左或右節點......
            tree.right = reConstructBinaryTreeCore(pre, in, preStart+1+leifLength, preEnd, root+1, inEnd);
        }

c、根據前序遍歷的思想,一定是先根節點,然後遍歷左子樹 (集合等於{L1}) ,再遍歷右子子樹(集合等於{R1}). L與L1元素一致,但排序順序不同

於是對L和L1,使用reConstructBinaryTreeCore(),由L第一個元素得到其子樹的節點tree.left(為父節點),由R得到tree.right.   

根據父節點tree.left在L1中的位置,將子樹進行劃分,得到下一級的節點(tree.left).left  和(tree.left).right;

同理,得到(tree.right).left  和(tree.right).right;

d、一直下去,得到最後的左右節點  這樣樹TreeNode就構建完成,由形如 tree.left or right這樣的形式構成  

如圖