diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/BuildTree.java b/Leecode/src/main/java/com/markilue/leecode/tree/BuildTree.java new file mode 100644 index 0000000..8f9c5e5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/BuildTree.java @@ -0,0 +1,276 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-25 09:58 + * @Description: TODO 力扣106题 从中序与后序遍历序列构造二叉树: + * 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗二叉树。 + * @Version: 1.0 + */ +public class BuildTree { + + + @Test + public void test(){ + int[] inorder = {9, 3, 15, 20, 7}, postorder = {9, 15, 7, 20, 3}; + TreeNode root = buildTree(inorder, postorder); + + System.out.println(levelOrder(root)); + + } + + @Test + public void test1(){ + int[] inorder = {2,1}, postorder = {2,1}; + TreeNode root = buildTree(inorder, postorder); + + System.out.println(levelOrder(root)); + + } + + @Test + public void test2(){ + int[] inorder = {15,9,10,3,20,5,7,8,4}, postorder = {15,10,9,5,4,8,7,20,3}; + TreeNode root = buildTree2(inorder, postorder); + + System.out.println(levelOrder(root)); + + } + + /** + * 对于这题本人没有任何思路,代码随想录思路:本质上就是寻找中间节点来划分左右树,而后序遍历的最后一个节点一定是中间节点: + * TODO 以后序数组的最后一个元素作为切割点,先切割中序数组,然后根据中序数组,反过来切割后序数组。一层一层切下去,每次后序数组额最后一个元素就是节点元素 + * 递归法: + * 速度超过5.22%,内存超过5.01% + * @param inorder + * @param postorder + * @return + */ + public TreeNode buildTree(int[] inorder, int[] postorder) { + + TreeNode root = new TreeNode(); + + if (inorder.length == 0) { + return root; + } + build1(inorder, postorder, root); + return root; + + } + + public void build(int[] inorder, int[] postorder, TreeNode node) { + + node.val = postorder[postorder.length - 1]; + int length=postorder.length; + + if (length == 1) { + return; + } + + //分别分割左右节点的inorder和postorder + int[] newIn1 = new int[length]; + int[] newPost1 = new int[length]; + + int[] newIn2 = new int[length]; + int[] newPost2 = new int[length]; + + int size = 0; + boolean flag = true; + for (int i = 0,j=0; i < length; i++) { + if(node.val==inorder[i]){ + flag=false; + continue; + } + if (flag) { + newIn1[i] = inorder[i]; + newPost1[i]=postorder[i]; + size+=1; + }else { + //这里-1因为root节点需要被跳过,但是post不用 + newIn2[j] = inorder[i]; + newPost2[j]=postorder[i-1]; + j++; + } + } + //这里需要判断是否大于0再做递归 + if(size>0){ + newIn1 = Arrays.copyOf(newIn1, size); + newPost1=Arrays.copyOf(newPost1,size); + node.left=new TreeNode(); + build(newIn1,newPost1,node.left); + } + + if(length-size-1>0){ + newIn2=Arrays.copyOf(newIn2,length-size-1); + newPost2=Arrays.copyOf(newPost2,length-size-1); + node.right=new TreeNode(); + build(newIn2,newPost2,node.right); + } + } + + //可以改进,只需要找到索引避免一直复制,速度击败9.2%,内存击败5.01% + public void build1(int[] inorder, int[] postorder, TreeNode node) { + + node.val = postorder[postorder.length - 1]; + int length=postorder.length; + + if (length == 1) { + return; + } + + //分别分割左右节点的inorder和postorder + int size = 0; + for (; size < length; size++) { + if(inorder[size]==node.val){ + break; + } + } + //这里需要判断是否大于0再做递归 + if(size>0){ + node.left=new TreeNode(); + //copyOfRange是左闭右开 + build(Arrays.copyOfRange(inorder,0, size),Arrays.copyOfRange(postorder,0,size),node.left); + } + + if(length-size-1>0){ + node.right=new TreeNode(); + build(Arrays.copyOfRange(inorder,size+1,length),Arrays.copyOfRange(postorder,size,length-1),node.right); + } + } + + + + int post_idx; + int[] postorder; + int[] inorder; + Map idx_map = new HashMap(); + + /** + * 官方的递归法,只用传索引,不传一个数组,避免了数组的一直赋值操作,加快进度 + * 速度超过99.46%。内存超过34.84% + * @param inorder + * @param postorder + * @return + */ + public TreeNode buildTree1(int[] inorder, int[] postorder) { + this.postorder = postorder; + this.inorder = inorder; + // 从后序遍历的最后一个元素开始 + post_idx = postorder.length - 1; + + // 建立(元素,下标)键值对的哈希表 + int idx = 0; + for (Integer val : inorder) { + idx_map.put(val, idx++); + } + + return helper(0, inorder.length - 1); + } + + public TreeNode helper(int in_left, int in_right) { + // 如果这里没有节点构造二叉树了,就结束 + if (in_left > in_right) { + return null; + } + + // 选择 post_idx 位置的元素作为当前子树根节点 + int root_val = postorder[post_idx]; + TreeNode root = new TreeNode(root_val); + + // 根据 root 所在位置分成左右两棵子树 + int index = idx_map.get(root_val); + + // 下标减一 + post_idx--; + // 构造右子树 + root.right = helper(index + 1, in_right); + // 构造左子树 + root.left = helper(in_left, index - 1); + return root; + } + + + /** + * 官方迭代法: + * 1)我们用一个栈和一个指针辅助进行二叉树的构造。初始时栈中存放了根节点(后序遍历的最后一个节点),指针指向中序遍历的最后一个节点; + * 2)我们依次枚举后序遍历中除了第一个节点以外的每个节点。如果 index 恰好指向栈顶节点, + * 那么我们不断地弹出栈顶节点并向左移动 index,并将当前节点作为最后一个弹出的节点的左儿子; + * 如果 index 和栈顶节点不同,我们将当前节点作为栈顶节点的右儿子; + * 3)无论是哪一种情况,我们最后都将当前的节点入栈。 + * + * 最后得到的二叉树即为答案。 + * + * @param inorder + * @param postorder + * @return + */ + public TreeNode buildTree2(int[] inorder, int[] postorder) { + if (postorder == null || postorder.length == 0) { + return null; + } + TreeNode root = new TreeNode(postorder[postorder.length - 1]); + Deque stack = new LinkedList(); + stack.push(root); + int inorderIndex = inorder.length - 1; + for (int i = postorder.length - 2; i >= 0; i--) { + int postorderVal = postorder[i]; + TreeNode node = stack.peek(); + //前面的如果不等,那么就是其右节点 + if (node.val != inorder[inorderIndex]) { + node.right = new TreeNode(postorderVal); + stack.push(node.right); + } else { + //右节点处理完毕,处理其左节点;如果出栈就证明遇上了需要处理的左节点 + while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) { + //同步处理stack和inorder + node = stack.pop(); + inorderIndex--; + } + node.left = new TreeNode(postorderVal); + stack.push(node.left); + } + } + return root; + } + + + + + + public List> levelOrder(TreeNode root) { + + List> result = new ArrayList<>(); + + //使用队列完成 + Deque deque = new LinkedList(); + + if (root != null) { + deque.addLast(root); + } + while (deque.size() > 0) { + List mid = new ArrayList<>(); + + int size = deque.size(); + + //注意这里必须使用size,而不是deque.size,因为deque的size一直在发生变化 + for (int i = 0; i < size; i++) { + TreeNode poll = deque.poll(); + mid.add(poll.val); + if (poll.left != null) deque.offer(poll.left); + if (poll.right != null) deque.offer(poll.right); + } + result.add(mid); + } + return result; + + } + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/BuildTreeII.java b/Leecode/src/main/java/com/markilue/leecode/tree/BuildTreeII.java new file mode 100644 index 0000000..166a003 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/BuildTreeII.java @@ -0,0 +1,132 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-25 12:45 + * @Description: TODO 力扣105题 从前序与中序遍历序列构造二叉树: + * 给定两个整数数组preorder 和 inorder,其中preorder 是二叉树的先序遍历, inorder是同一棵树的中序遍历,请构造二叉树并返回其根节点。 + * @Version: 1.0 + */ +public class BuildTreeII { + + @Test + public void test() { + int[] preorder = {3, 9, 20, 15, 7}, inorder = {9, 3, 15, 20, 7}; + TreeNode node = buildTree(preorder, inorder); + + System.out.println(levelOrder(node)); + } + + + /** + * 核心其实一致,即中序遍历可以确定左右节点,前后序遍历可以确定根节点;因此根据前后序一直找根节点即可 + * 这里运用递归法: + * 速度超过99.46%,内存超过60.36% + * + * @param preorder + * @param inorder + * @return + */ + int[] preorder; + // + Map map = new HashMap(); + int wanted=0; + + public TreeNode buildTree(int[] preorder, int[] inorder) { + this.preorder = preorder; + for (int i = 0; i < inorder.length; i++) { + map.put(inorder[i], i); + } + + return build(0, inorder.length-1); + } + + public TreeNode build(int leftIndex, int rightIndex) { + + if(leftIndex>rightIndex){ + return null; + } + TreeNode node = new TreeNode(preorder[wanted]); + Integer index = map.get(preorder[wanted++]); + node.left=build(leftIndex,index-1); + node.right=build(index+1,rightIndex); + + return node; + } + + + /** + * 官方迭代法: + * 1)我们用一个栈和一个指针辅助进行二叉树的构造。初始时栈中存放了根节点(前序遍历的第一个节点),指针指向中序遍历的第一个节点; + * 2)我们依次枚举前序遍历中除了第一个节点以外的每个节点。 + * 如果 index 恰好指向栈顶节点,那么我们不断地弹出栈顶节点并向右移动 index,并将当前节点作为最后一个弹出的节点的右儿子; + * 如果 index 和栈顶节点不同,我们将当前节点作为栈顶节点的左儿子; + * 3)无论是哪一种情况,我们最后都将当前的节点入栈。 + * + * @param preorder + * @param inorder + * @return + */ + public TreeNode buildTree1(int[] preorder, int[] inorder) { + if (preorder == null || preorder.length == 0) { + return null; + } + TreeNode root = new TreeNode(preorder[0]); + Deque stack = new LinkedList(); + stack.push(root); + int inorderIndex = 0; + for (int i = 1; i < preorder.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; + } + + + + public List> levelOrder(TreeNode root) { + + List> result = new ArrayList<>(); + + //使用队列完成 + Deque deque = new LinkedList(); + + if (root != null) { + deque.addLast(root); + } + while (deque.size() > 0) { + List mid = new ArrayList<>(); + + int size = deque.size(); + + //注意这里必须使用size,而不是deque.size,因为deque的size一直在发生变化 + for (int i = 0; i < size; i++) { + TreeNode poll = deque.poll(); + mid.add(poll.val); + if (poll.left != null) deque.offer(poll.left); + if (poll.right != null) deque.offer(poll.right); + } + result.add(mid); + } + return result; + + } + +}