From 4032d74f99a242ae8cb74dcb814799c5c6ed1b66 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Sat, 11 Mar 2023 22:19:27 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../leecode/hot100/T43_IsSymmetric.java | 42 +++++++ .../leecode/hot100/T44_LevelOrder.java | 87 ++++++++++++++ .../markilue/leecode/hot100/T45_MaxDepth.java | 47 ++++++++ .../leecode/hot100/T46_BuildTree.java | 75 ++++++++++++ .../markilue/leecode/hot100/T47_Flatten.java | 98 ++++++++++++++++ .../leecode/hot100/T48_MaxProfit.java | 91 +++++++++++++++ .../leecode/hot100/T49_MaxPathSum.java | 108 ++++++++++++++++++ 7 files changed, 548 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T43_IsSymmetric.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T44_LevelOrder.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T45_MaxDepth.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T46_BuildTree.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T47_Flatten.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T48_MaxProfit.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/hot100/T49_MaxPathSum.java diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T43_IsSymmetric.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T43_IsSymmetric.java new file mode 100644 index 0000000..bc1ac6d --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T43_IsSymmetric.java @@ -0,0 +1,42 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 09:53 + *@Description: + * TODO 力扣101题 对称二叉树: + * 给你一个二叉树的根节点 root , 检查它是否轴对称。 + *@Version: 1.0 + */ +public class T43_IsSymmetric { + + + /** + * 递归法:速度击败100% 内存击败43.52% + * @param root + * @return + */ + public boolean isSymmetric(TreeNode root) { + return isSymmetric(root.left, root.right); + } + + public boolean isSymmetric(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) { + return true; + } else if (root1 == null) { + return false; + } else if (root2 == null) { + return false; + } + + if (root1.val != root2.val) { + return false; + } + + return isSymmetric(root1.left, root2.right) && isSymmetric(root1.right, root2.left); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T44_LevelOrder.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T44_LevelOrder.java new file mode 100644 index 0000000..3181563 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T44_LevelOrder.java @@ -0,0 +1,87 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 10:00 + *@Description: + * TODO 力扣102题 二叉树的层序遍历: + * 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 + *@Version: 1.0 + */ +public class T44_LevelOrder { + + + /** + * 队列迭代法: + * 速度击败100% 内存击败72.7% 0ms + * @param root + * @return + */ + public List> levelOrder(TreeNode root) { + + List> result = new ArrayList<>(); + + if (root == null) { + return result; + } + + Queue queue = new LinkedList<>(); + + queue.add(root); + + while (!queue.isEmpty()) { + int size = queue.size(); + List level = new ArrayList<>(); + + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + level.add(node.val); + if (node.left != null) queue.add(node.left); + if (node.right != null) queue.add(node.right); + } + result.add(level); + } + + return result; + + + } + + + + List> result=new ArrayList<>(); + + /** + * dfs: + * 速度击败100% 内存击败65.56% 0ms + * @param root + * @return + */ + public List> levelOrder1(TreeNode root) { + level(root,0); + return result; + } + + public void level(TreeNode root,int level){ + if(root==null){ + return; + } + if(level==result.size()){ + result.add(new ArrayList<>()); + } + //这个level以前处理过,加入在原来的level位置 + result.get(level).add(root.val); + level(root.left,level+1); + level(root.right,level+1); + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T45_MaxDepth.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T45_MaxDepth.java new file mode 100644 index 0000000..0004f1b --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T45_MaxDepth.java @@ -0,0 +1,47 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 10:12 + *@Description: + * TODO 力扣104题 二叉树的最大深度: + * 给定一个二叉树,找出其最大深度。 + * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + * 说明: 叶子节点是指没有子节点的节点。 + *@Version: 1.0 + */ +public class T45_MaxDepth { + + + int maxDepth = 1; + + /** + * 思路:队列法也可以,这里使用dfs + * @param root + * @return + */ + public int maxDepth(TreeNode root) { + if (root == null) return 0; + dfs(root, 1); + return maxDepth; + } + + + public void dfs(TreeNode root, int level) { + + if (root == null) { + return; + } + + if (maxDepth < level) { + maxDepth = level; + } + dfs(root.left, level + 1); + dfs(root.right, level + 1); + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T46_BuildTree.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T46_BuildTree.java new file mode 100644 index 0000000..6d04d9a --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T46_BuildTree.java @@ -0,0 +1,75 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 10:21 + *@Description: + * TODO 力扣105题 从前序与中序遍历序列构造二叉树: + * 给定两个整数数组 preorder 和 inorder + * 其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历 + * 请构造二叉树并返回其根节点。 + *@Version: 1.0 + */ +public class T46_BuildTree { + + @Test + public void test() { + int[] preorder = {3, 9, 20, 15, 7}; + int[] inorder = {9, 3, 15, 20, 7}; + TreeUtils.printTreeByLevel(buildTree(preorder, inorder)); + } + + @Test + public void test1() { + int[] preorder = {1, 2}; + int[] inorder = {2, 1}; + TreeUtils.printTreeByLevel(buildTree(preorder, inorder)); + } + + /** + * 思路:根据前序遍历的root来分割中序遍历 + * 速度击败14.83% 内存击败54.64% 6ms + * @param preorder + * @param inorder + * @return + */ + public TreeNode buildTree(int[] preorder, int[] inorder) { + return buildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1); + } + + + public TreeNode buildTree(int[] preorder, int[] inorder, int preLeft, int preRight, int inLeft, int inRight) { + if (preLeft > preRight || inLeft > inRight) return null; + TreeNode root = new TreeNode(preorder[preLeft]); + if (preLeft == preRight) { + return root; + } + + //在inorder里面找root对应的索引,分割左右子树 + int index = findIndex(inorder, inLeft, inRight, preorder[preLeft]); + + //构造root的左子树 + root.left = buildTree(preorder, inorder, preLeft + 1, preLeft + (index - inLeft), inLeft, index - 1); + + + //构造root的右子树 + root.right = buildTree(preorder, inorder, preLeft + (index - inLeft) + 1, preRight, index + 1, inRight); + + return root; + + } + + private int findIndex(int[] inorder, int inLeft, int inRight, int value) { + + for (int i = inLeft; i <= inRight; i++) { + if (inorder[i] == value) return i; + } + return -1; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T47_Flatten.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T47_Flatten.java new file mode 100644 index 0000000..04fcec1 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T47_Flatten.java @@ -0,0 +1,98 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo; +import org.junit.Test; + +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 10:50 + *@Description: + * TODO 力扣114题 二叉树展开为链表: + * 给你二叉树的根结点 root ,请你将它展开为一个单链表: + * + * 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 + * 展开后的单链表应该与二叉树 先序遍历 顺序相同。 + *@Version: 1.0 + */ +public class T47_Flatten { + + @Test + public void test() { + TreeNode treeNode = TreeUtils.structureTree(Arrays.asList(1, 2, 5, 3, 4, null, 6), 0); + getResult(treeNode); + TreeUtils.printTreeByLevel(treeNode); + } + + @Test + public void test1() { + TreeNode treeNode = TreeUtils.structureTree(Arrays.asList(1, null,2,null,null,3), 0); + getResult(treeNode); + TreeUtils.printTreeByLevel(treeNode); + } + + + /** + * 思路: 稍微纠结了一会,本来想着直接前序遍历记录下来,在赋值给result,但是不行 + * TODO 后来的思路: 展平左边,展平右边,将左边的右边设置为当前的右边,再把当前root的right设置有左边,再把左边删除 + * 速度击败100% 内存击败85.56% + * @param root + */ + public void flatten(TreeNode root) { + getResult(root); + } + + public TreeNode getResult(TreeNode root) { + if (root == null) { + return null; + } + + TreeNode node = getResult(root.left); + TreeNode temp=node; + while (node != null && node.right != null) { + node = node.right; + } + //有左子树就放 + if (node != null) { + node.right = getResult(root.right); + root.right = temp; + root.left = null; + }else { + //TODO 需要注意:没有也就是单独调一下右边 + getResult(root.right); + } + return root; + } + + + /** + * 官方题解:记录前驱节点 + * 非常的精妙,思路上可以借鉴morris遍历 + * 速度击败1005 内存击败86.16% + * @param root + */ + public void flatten1(TreeNode root) { + TreeNode curr = root; + while (curr != null) { + if (curr.left != null) { + TreeNode next = curr.left; + TreeNode predecessor = next; + //找到左节点的最右节点,他就是左节点前序遍历的最后一个,将他的右边设置为curr.right即可 + while (predecessor.right != null) { + predecessor = predecessor.right; + } + predecessor.right = curr.right; + curr.left = null; + curr.right = next; + } + curr = curr.right; + } + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T48_MaxProfit.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T48_MaxProfit.java new file mode 100644 index 0000000..b629353 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T48_MaxProfit.java @@ -0,0 +1,91 @@ +package com.markilue.leecode.hot100; + +import org.junit.Test; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 11:33 + *@Description: + * TODO 力扣121 买卖股票的最佳时机: + * 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 + * 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 + * 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。 + *@Version: 1.0 + */ +public class T48_MaxProfit { + + @Test + public void test(){ + int[] prices={7,1,5,3,6,4}; + System.out.println(maxProfit1(prices)); + } + + + /** + * 思路:实施贪心 + * 速度击败100% 内存击败56.4% 1ms + * @param prices + * @return + */ + public int maxProfit(int[] prices) { + + int minPrice = prices[0]; + int curProfit = 0; + int maxProfit = 0; + + for (int price : prices) { + curProfit = price - minPrice; + if (maxProfit < curProfit) maxProfit = curProfit; + if (minPrice > price) minPrice = price; + } + + return maxProfit; + + } + + + /** + * 思路:动态规划 + * + * 速度击败16.79% 内存击败93.59% 23ms + * @param prices + * @return + */ + public int maxProfit1(int[] prices) { + + int[][] dp = new int[prices.length][2]; + dp[0][0] = 0;//没股票 + dp[0][1] = -prices[0];//有股票 + + for (int i = 1; i < prices.length; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i-1][1] + prices[i]); + dp[i][1] = Math.max(dp[i - 1][1], -prices[i]); + } + + return dp[prices.length-1][0]; + + } + + + /** + * 滚动数组优化 + * 速度击败55.23% 内存击败23.81% 2ms + * @param prices + * @return + */ + public int maxProfit2(int[] prices) { + + int dp0 = 0;//没股票 + int dp1 = -prices[0];//有股票 + + for (int i = 1; i < prices.length; i++) { + dp0 = Math.max(dp0, dp1 + prices[i]); + dp1 = Math.max(dp1, -prices[i]); + } + + return dp0; + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/hot100/T49_MaxPathSum.java b/Leecode/src/main/java/com/markilue/leecode/hot100/T49_MaxPathSum.java new file mode 100644 index 0000000..613f1f5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/hot100/T49_MaxPathSum.java @@ -0,0 +1,108 @@ +package com.markilue.leecode.hot100; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import org.junit.Test; + +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.hot100 + *@Author: markilue + *@CreateTime: 2023-03-11 11:47 + *@Description: + * TODO 力扣124题 二叉树中的最大路径和: + * 路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。 + * 同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 + * 路径和 是路径中各节点值的总和。 + * 给你一个二叉树的根节点 root ,返回其 最大路径和 。 + *@Version: 1.0 + */ +public class T49_MaxPathSum { + + @Test + public void test() { + TreeNode root = TreeUtils.structureTree(Arrays.asList(-10, 9, 20, null, null, 15, 7), 0); +// System.out.println(maxPathSum1(root, true)); + } + + + int maxSum = Integer.MIN_VALUE; + + /** + * 思路:本质上就是判断左节点之和 和当前值 来判断左端要不要 + * 先中序遍历dfs + * @param root + * @return + */ + public int maxPathSum(TreeNode root) { + + if (root == null) { + return 0; + } + //遍历左子树决定当前节点要不要 + int leftSum = maxPathSum(root.left); + int total = leftSum + root.val; + + return total > 0 ? total + maxPathSum(root.right) : maxPathSum(root.right); + + } + + /** + * 思路:状态可以分为要当前节点(加两边)和不要当前节点(加一边),两边都不要 + * 有问题,想不清楚 + * @param root + * @return + */ + public int[] maxPathSum1(TreeNode root) { + if (root == null) { + return new int[]{0, 0, 0};// + } + + int[] left = maxPathSum1(root); + int[] right = maxPathSum1(root); + int zero = Math.max(0, root.val); + + int one = Math.max(Math.max(left[0],right[0])+root.val,Math.max(left[1],right[1])+ root.val); + int two = left[0] + right[0] + root.val; + + return new int[]{zero,one,two}; + + + } + + + /** + * 官方题解: + * 递归 + * @param root + * @return + */ + public int maxPathSum2(TreeNode root) { + maxGain(root); + return maxSum; + } + + public int maxGain(TreeNode node) { + if (node == null) { + return 0; + } + + // 递归计算左右子节点的最大贡献值 + // 只有在最大贡献值大于 0 时,才会选取对应子节点 + int leftGain = Math.max(maxGain(node.left), 0); + int rightGain = Math.max(maxGain(node.right), 0); + + // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值 + int priceNewpath = node.val + leftGain + rightGain; + + // 更新答案 + maxSum = Math.max(maxSum, priceNewpath);//全要的对比 + + // 返回节点的最大贡献值 + return node.val + Math.max(leftGain, rightGain);//只要一边的返回 + } + + +}