105. Construct Binary Tree from Preorder and Inorder Traversal 重建二叉樹

Note:《劍指offer》面試題7 重建二叉樹
題目:給出了前序和中序周遊序列,根據這兩個序列重建一棵二叉樹
關鍵點:前序周遊時,根節點爲序列的第一個值;中序周遊時,根節點左邊的序列是根節點的左子樹節點,右邊是右子樹節點



第1、2種方法思路一致,只不過用了Map以提高檢索父節點和子節點index的效率。
思路:遍歷除了第一個值的前序周遊子序列,將所有其他值和根節點值對比,通過在中序周遊序列中它們的index判斷(節點index先於根節點index,則爲其左子樹,反之右子樹,這個判斷由中序周遊的特點決定的)。節點在根節點在其左子樹或右子樹,如果根節點沒有左兒子或者右兒子節點,則該節點成爲其子節點;否則,節點將要對比的新根節點變成原根節點的兒子節點,直到它成爲某個對比的根節點的子節點。時間複雜度應該是O(n*h),n爲節點數,h爲樹的高度。

第一種方法,超時因爲indexOf

public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        TreeNode parent = root;
        // 從先序周遊開始
        for(int i = 1; i < preorder.length; i++){
            TreeNode node = new TreeNode(preorder[i]);
            while(true){
                boolean isLeft = isLeftSubTree(inorder, parent.val, node.val);
                if(isLeft){ // 如果是左子樹
                    if(parent.left == null){ // 左子樹爲空,則它爲父節點的左兒子
                        parent.left = node;
                        break;
                    }else{   // 左子樹有自己的根節點,則將和其對比
                        parent = parent.left;
                    }
                }else{
                    if(parent.right == null){
                        parent.right = node;
                        break;
                    }else{
                        parent = parent.right;
                    }
                }
            }
            parent = root;
        }
        return root;
    }
/** 節點是root節點的左子樹還是右子樹 **/
    private boolean isLeftSubTree(int[] inorder, int root, int node) {
        int rootIndex = indexOf(inorder, root);
        int nodeIndex = indexOf(inorder, node);
        // 如果節點在中序周遊的索引位於根節點的後面,表示它在根節點的右子樹
        if(nodeIndex > rootIndex){
            return false;
        }else if(nodeIndex < rootIndex){ // 反之
            return true;
        }else{
            return false;
        } // 按理來說不存在
    }

    /** 找到某數在數組中的索引 **/
    private int indexOf(int[] array, int elem){
        for (int i = 0; i < array.length; i++) {
            if (elem == array[i])
                return i;
        }
        return -1;
    }      

第二種方法,103ms

public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || preorder.length == 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        TreeNode parent = root;
        Map<Integer, Integer> inorderIndexMap = buildIndexMap(inorder);
        // 從先序周遊開始
        for(int i = 1; i < preorder.length; i++){
            TreeNode node = new TreeNode(preorder[i]);
            while(true){
                boolean isLeft = isLeftSubTree(inorderIndexMap, parent.val, node.val);
                if(isLeft){
                    if(parent.left == null){
                        parent.left = node;
                        break;
                    }
                    else{
                        parent = parent.left;
                    }
                }else{
                    if(parent.right == null){
                        parent.right = node;
                        break;
                    }
                    else{
                        parent = parent.right;
                    }
                }
            }
            parent = root;
        }
        return root;
    }

    private boolean isLeftSubTree(Map<Integer, Integer> inorderIndexMap, int root, int node) {
        int rootIndex = inorderIndexMap.get(root);
        int nodeIndex = inorderIndexMap.get(node);
        // 如果節點在中序周遊的索引位於根節點的後面,表示它在根節點的右子樹
        if(nodeIndex > rootIndex){
            return false;
        }else if(nodeIndex < rootIndex){ // 反之
            return true;
        }else{
            return false;
        } // 按理來說不存在
    }

    /** 用不重複的數組建其值與index的map **/
    private Map<Integer, Integer> buildIndexMap(int[] array){
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int index = 0;
        for(int x : array){
            map.put(x, index);
            index++;
        }
        return map;
    }



第3種,方法看了《劍指offer》面試題7——重建二叉樹的思路後實現的
思路:在樹的前序遍歷中,根節點爲第一個,後面連續的一部分子序列爲左子樹節點,左子樹序列後面連續的子序列爲右子樹節點。在中序遍歷中,根節點以左序列爲根節點的左子樹,以右序列爲右子樹節點。那麼,在前序周遊序列中得到根節點值,可以通過該值在中序周遊序列中找到其index,那麼index之前爲左子樹,通過計算能得到左右子樹節點數。然後,重建左右子樹,用遞歸的方法重建整個二叉樹。時間複雜度O(n)

第三種方法,6ms

private Map<Integer, Integer> inorderIndexMap = null;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || preorder.length == 0){
            return null;
        }
        inorderIndexMap = buildIndexMap(inorder);
        return buildTree(preorder.length, preorder, 0, inorder, 0);
    }

    private TreeNode buildTree(int len, int[] preorder, int pStart,
                               int[] inorder, int iStart) {

        if(len <= 0){
            return null;
        }
        TreeNode root = new TreeNode(preorder[pStart]); // 在preorder中第pStart個爲子樹的root
        int rootIndex = inorderIndexMap.get(root.val); //找到inorder中子樹root的位置
        int leftLen = rootIndex-iStart;
        root.left = buildTree(leftLen, preorder, pStart+1, inorder, iStart);
        root.right = buildTree(len-1-leftLen, preorder, pStart+1+leftLen, inorder, rootIndex+1);
        return root;
    }


    /** 用不重複的數組建其值與index的map **/
    private Map<Integer, Integer> buildIndexMap(int[] array){
        Map<Integer, Integer> map = new HashMap<Integer, Integer>();
        int index = 0;
        for(int x : array){
            map.put(x, index);
            index++;
        }
        return map;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章