[Sword offer] 07 Rebuild the binary tree

1. The problem?

1. Enter the results of pre-order traversal and mid-order traversal of a binary tree, and please rebuild the binary tree.

2. Examples

Preorder traversal preorder = [3,9,20,15,7]

Inorder traversal inorder = [9,3,15,20,7]

Insert picture description here

3. Restrictions

0 <= number of nodes <= 5000

Two, the solution

method one

1. Determine the start and end of the subscript range of the preorder traversal. If the start is greater than the end, there is no node in the current binary tree, and the null value null is returned. If the start is equal to the end, there is exactly one node in the current binary tree, and the node is created as the root node based on the node value and returned.

2. If the start is less than the end, there are multiple nodes in the current binary tree. In the middle-order traversal, the position of the root node is obtained, and the subscript range and the number of nodes of the left and right subtrees are obtained. After the number of nodes is known, the left and right subtrees can be obtained in the preorder traversal. Then, recursively reconstruct the left and right subtrees, and use the root nodes of the left and right subtrees as the left and right child nodes of the current root node.

Complexity analysis

Time complexity: O(n). For each node, there is a creation process and a reconstruction process based on the left and right subtrees.
Space complexity: O(n). The cost of storing the entire tree.

Method 2:
1. Use the stack to save the traversed nodes. The pointer of the middle order traversal at the initial time points to the first element, and the array traversed in the preorder traverse. If the element traversed in the preorder is not equal to the element pointed to by the pointer of the middle order traversal, the element traversed in the preorder is the left child of the previous node node. If the element of the previous traversal is equal to the element pointed to by the pointer of the middle traversal, then the elements of the middle traversal are traversed forward, and the elements traversed in the previous traversal are reversed, and the last equal element is found, and the next node of the previous traversal As the right child of the last equal element. Among them, the reverse traversal of the elements traversed in the previous order can be realized by popping elements of the stack.

2. Create a root node using the first element traversed in the preorder.

3. Create a stack and push the root node into the stack.

4. Initialize the sequence traversal subscript to 0.

5. Traverse each element of the previous traversal, and determine whether the previous element (ie the top element of the stack) is equal to the element pointed to by the middle-order traversal subscript.

6. If the previous element is not equal to the element pointed to by the in-order traversal subscript, the current element is used as the left child node of the previous element, and the current element is pushed onto the stack.

7. If the previous element is equal to the element pointed to by the in-order traversal subscript, an element is popped from the stack, and the in-order traversal subscript is made to point to the next element, and then continue to determine whether the top element of the stack is equal to the in-order traversal subscript pointing If the elements of are equal, repeat the operation until the stack is empty or the elements are not equal. Then let the current element be the last right node that wants to wait for the element.

8. After the traversal is over, return to the root node.

package com.haoxiansheng.demo01.SwordfingerOffer;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

/**
 * @author flame
 * @data 2020/10/19
 */
@Slf4j
public class BuildTree {
    
    
    public static void main(String[] args) {
    
    
        int [] preorder = {
    
    3,9,20,15,7};
        int [] inorder = {
    
    9,3,15,20,7};
       log.info("buildTree=>{}", buildTree(preorder, inorder));
        log.info("buildTree2=>{}", buildTree2(preorder, inorder));
    }

    @Data
    static class TreeNode {
    
    
        private int val;
        TreeNode left;
        TreeNode right;

        public TreeNode(int val) {
    
    
            this.val = val;
        }
    }
    //  方法一:递归

    /**
     * 前序遍历: 根  左  右
     * 中序遍历: 左  根  右
     * 后序遍历: 左  右  根
     * <p>
     * 复杂度分析
     * 时间复杂度:O(n)。对于每个节点都有创建过程以及根据左右子树重建过程。
     * 空间复杂度:O(n)。存储整棵树的开销。
     *
     * @param preorder
     * @param inorder
     * @return
     */
    public static TreeNode buildTree(int[] preorder, int[] inorder) {
    
    
        // 1、若为null 直接返回
        if (preorder == null || preorder.length == 0) {
    
    
            return null;
        }

        // 用Map  存放
        // 使用一个 Map 存储中序遍历的每个元素及其对应的下标,目的是为了快速获得一个元素在中序遍历中的位置。
        //调用递归方法,对于前序遍历和中序遍历,下标范围都是从 0 到 n-1,其中 n 是二叉树节点个数。
        Map<Integer, Integer> tempMap = new HashMap<Integer, Integer>();

        int len = preorder.length;
        for (int i = 0; i < len; i++) {
    
    
            tempMap.put(inorder[i], i);
        }
        TreeNode root = buildTree(preorder, 0, len - 1, inorder, 0, len - 1, tempMap);
        return root;
    }

    public static TreeNode buildTree(int[] preorder, int preorderStart, int preorderEnd, int[] inorder, int inorderStart, int inorderEnd, Map<Integer, Integer> tempMap) {
    
    
        // preorderStart > preorderEnd 说明为null
        if (preorderStart > preorderEnd) {
    
    
            return null;
        }
        // 先序遍历 开始为根
        int rootVal = preorder[preorderStart];
        TreeNode root = new TreeNode(rootVal);
        // 如果开始和结尾值相等则说明只有一个
        if (preorderStart != preorderEnd) {
    
    
            // 活得根节点的索引
            int rootIndex = tempMap.get(rootVal);
            // 得到左右节点个数
            int leftNodes = rootIndex - inorderStart;
            int rightNodes = inorderEnd - rootIndex;
            // 求左子树  左子树开始  左子树结束
            TreeNode leftChildTree = buildTree(preorder, preorderStart + 1, preorderStart + leftNodes, inorder, inorderStart, rootIndex - 1, tempMap);
            // 求右子树  右子树的开始位置和结束位置
            TreeNode rightChildTree = buildTree(preorder, preorderEnd - rightNodes + 1, preorderEnd, inorder, rootIndex + 1, inorderEnd, tempMap);

            root.left = leftChildTree;
            root.right = rightChildTree;
            // 封装返回

        }
        return root;
    }


    // 方法二:迭代
    // 时间复杂度:O(n)。前序遍历和后序遍历都被遍历。
    //空间复杂度:O(n)。额外使用栈存储已经遍历过的节点。

    public static TreeNode buildTree2(int[] preorder, int[] inorder) {
    
    
        if (preorder == null || preorder.length == 0) {
    
    
            return null;
        }
        TreeNode root = new TreeNode(preorder[0]);
        int length = preorder.length;
        Stack<TreeNode> stack = new Stack<TreeNode>();
        stack.push(root);
        int inorderIndex = 0;
        for (int i = 1; i < length; i++) {
    
    
            int preorderVal = preorder[i];
            TreeNode node = stack.peek();
            if (node.val != inorder[inorderIndex]) {
    
    
                node.left = new TreeNode(preorderVal);
                stack.push(node.left);
            } else {
    
    
                while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
    
    
                    node = stack.pop();
                    inorderIndex++;
                }
                node.right = new TreeNode(preorderVal);
                stack.push(node.right);
            }
        }
        return root;
    }


}


Guess you like

Origin blog.csdn.net/qq_40996741/article/details/109171182