From 620b260e4af350f0186b4c1c0e21e1c310789823 Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Sun, 11 Dec 2022 15:44:41 +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 --- .../com/markilue/leecode/dynamic/T18_Rob.java | 128 +++++++++++++++ .../com/markilue/leecode/dynamic/T19_Rob.java | 111 +++++++++++++ .../com/markilue/leecode/dynamic/T20_Rob.java | 150 ++++++++++++++++++ .../com/markilue/leecode/tree/TreeNode.java | 17 +- .../com/markilue/leecode/tree/TreeUtils.java | 51 ++++++ 5 files changed, 451 insertions(+), 6 deletions(-) create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T18_Rob.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T19_Rob.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/dynamic/T20_Rob.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/TreeUtils.java diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T18_Rob.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T18_Rob.java new file mode 100644 index 0000000..67185d5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T18_Rob.java @@ -0,0 +1,128 @@ +package com.markilue.leecode.dynamic; + +import org.junit.Test; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.dynamic + * @Author: dingjiawen + * @CreateTime: 2022-12-11 10:04 + * @Description: TODO 力扣198题 打家劫舍: + * 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, + * 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + * @Version: 1.0 + */ +public class T18_Rob { + + @Test + public void test() { + + int[] nums = {1, 2, 3, 1}; + System.out.println(rob(nums)); + + } + + @Test + public void test1() { + + int[] nums = {2,7,9,3,1}; + System.out.println(rob1(nums)); + + } + + @Test + public void test2() { + + int[] nums = {4,1,2,3}; + System.out.println(rob1(nums)); + + } + + + /** + * 思路:本质上就是只能隔一个取,甚至可以不用动态规划,直接奇偶分别相加,这里先试试直接奇偶行不行? + * 不行,因为只是相邻不能进,事实上隔两个就可以,如果两个数恰好隔两个,使用奇偶就错开了,尝试动态规划法 + * TODO 动态规划五部曲: + * (1)dp定义:dp[i]表示在i这个位置所能得到的最大金钱 + * (2)dp状态转移方程:dp[i]=max(dp[i-3]+nums[i],dp[i-2]+nums[i],dp[i-1]),即当前最大可能是上一个最大或者隔一个的加上自己,或者隔两个的加上自己 + * (3)dp初始化:dp[0]=nums[0];dp[1]=nums[1]>num[0]?nums[1]:nums[0] + * (4)dp的遍历顺序:从前往后 + * (4)dp举例推导:若nums={2,7,9,3,1},则dp={2,7,11,11,12} + * 速度击败100%,内存击败43.6% + * @param nums + * @return + */ + public int rob(int[] nums) { + if(nums.length==1){ + return nums[0]; + } + if(nums.length==2){ + return nums[0]>nums[1]?nums[0]:nums[1]; + } + + int[] dp = new int[nums.length]; + dp[0]=nums[0]; + dp[1]=Math.max(nums[0],nums[1]); + dp[2]=Math.max(nums[1],nums[0]+nums[2]); + + for (int i = 3; i < dp.length; i++) { + dp[i]=Math.max(Math.max(dp[i-3]+nums[i],dp[i-2]+nums[i]),dp[i-1]); + } + + return dp[nums.length-1]; + } + + + /** + * 代码随想录动态规划法:本人想复杂了,状态转换没有那么麻烦 + * 因为由于有Math.max(dp[i-2]+nums[i],dp[i-1]); + * 所以就算dp[i-3]>dp[i-2],也会因为这个转移方程,将dp[i-2]赋值为dp[i-3],所以本人的有所多余 + * dp举例推导:若nums={2,7,9,3,1},则dp={2,7,11,11,12} + * 速度击败100%,内存击败54.93% + * @param nums + * @return + */ + public int rob1(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if(nums.length==1){ + return nums[0]; + } + int[] dp = new int[nums.length]; + dp[0]=nums[0]; + dp[1]=Math.max(nums[0],nums[1]); + + for (int i = 2; i < dp.length; i++) { + dp[i]=Math.max(dp[i-2]+nums[i],dp[i-1]); + } + + return dp[nums.length-1]; + } + + + /** + * 官方动态规划法:将空间复杂度降到O(1) + * 速度击败100%,内存击败55.39% + * @param nums + * @return + */ + public int rob2(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int length = nums.length; + if (length == 1) { + return nums[0]; + } + int first = nums[0], second = Math.max(nums[0], nums[1]); + for (int i = 2; i < length; i++) { + int temp = second; + second = Math.max(first + nums[i], second); + first = temp; + } + return second; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T19_Rob.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T19_Rob.java new file mode 100644 index 0000000..c34dd81 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T19_Rob.java @@ -0,0 +1,111 @@ +package com.markilue.leecode.dynamic; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.dynamic + * @Author: dingjiawen + * @CreateTime: 2022-12-11 10:58 + * @Description: + * TODO 力扣213题 打家劫舍II: + * 你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 , + * 这意味着第一个房屋和最后一个房屋是紧挨着的。 + * 同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 + * 给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。 + * @Version: 1.0 + */ +public class T19_Rob { + + @Test + public void test(){ + int[] nums = {1, 2, 3, 1}; + System.out.println(rob2(nums)); + } + + @Test + public void test1(){ + int[] nums = {0,0}; + System.out.println(rob1(nums)); + } + + /** + * 思路:与打家劫舍类似,只是边界条件不同,最后一个与第一个连起来了 + * 那么状态转移方程之类的都不变 + * 用了最后一个有什么特点——dp[last]!=dp[last-1] + * 但是没法记录中间使用过的数,就算记录了好像也没办法增加或者删掉,这里浅试一下 + * @param nums + * @return + */ + public int rob(int[] nums) { + if(nums==null||nums.length==0)return 0; + if(nums.length==1)return nums[0]; + + int[][] dp = new int[nums.length][nums.length/2]; //dp数组[max][使用过的下标],这里不对;没有想到什么好的数据结构 +// dp[0]=nums[0]; +// dp[1]=Math.max(nums[0],nums[1]); +// +// for (int i = 2; i < nums.length; i++) { +// dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]); +// } +// int last= nums.length-1; +// +// return dp[last]-nums[last]>dp[last-1]?dp[last]-nums[last]:dp[last-1]; + return 0; + + } + + /** + * 代码随想录思路:与打家劫舍类似,只是边界条件不同,最后一个与第一个连起来了 + * 那么只需要分别计算两次只有头和只有尾的dp,取最大即可 + * 速度击败100%,内存击败10% + * @param nums + * @return + */ + public int rob1(int[] nums){ + if(nums==null||nums.length==0)return 0; + if(nums.length==1)return nums[0]; + + int dpHead = dp(nums,0, nums.length-1); + int dpTail=dp(nums,1, nums.length); + return Math.max(dpHead,dpTail); + } + private int dp(int[] nums,int start,int end) { + if(start==end-1)return nums[start]; + int length=end-start; + + int[] dp = new int[length]; //dp数组[max][使用过的下标],这里不对;没有想到什么好的数据结构 + dp[0]=nums[start]; + dp[1]=Math.max(nums[start],nums[start+1]); + + for (int i = 2; i < length; i++) { + dp[i]=Math.max(dp[i-1],dp[i-2]+nums[start+i]); + } + return dp[length-1]; + + } + + + /** + * 评论区一次dp完成法:分两种情况,要么不抢0位置,要么不抢n-1位置,这样就可以包括所有的情况了。 + * (而不是抢0或者抢n-1) + * 速度击败100%,内存击败90% + * @param nums + * @return + */ + public int rob2(int[] nums) { + int n = nums.length; + if (n == 1) return nums[0]; + int[] dp1 = new int[n], dp2 = new int[n]; // dp1不抢n-1,dp2不抢0 + dp1[0] = nums[0]; + dp1[1] = Math.max(nums[1], nums[0]); + dp2[1] = nums[1]; + for (int i = 2; i < n; ++i) { + dp1[i] = Math.max(dp1[i - 1], dp1[i - 2] + nums[i]); + dp2[i] = Math.max(dp2[i - 1], dp2[i - 2] + nums[i]); + } + return Math.max(dp1[n - 2], dp2[n - 1]); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/dynamic/T20_Rob.java b/Leecode/src/main/java/com/markilue/leecode/dynamic/T20_Rob.java new file mode 100644 index 0000000..1eb2b1f --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/dynamic/T20_Rob.java @@ -0,0 +1,150 @@ +package com.markilue.leecode.dynamic; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import org.junit.Test; + +import java.util.*; + + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.dynamic + * @Author: dingjiawen + * @CreateTime: 2022-12-11 12:15 + * @Description: TODO 力扣337题 打家劫舍III: + * 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。 + * 除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 + * 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。 + * 给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。 + * @Version: 1.0 + */ +public class T20_Rob { + + @Test + public void test() { + List valList = new ArrayList<>(Arrays.asList(3, 2, 3, null, 3, null, 1)); + TreeNode root = TreeUtils.structureTree(valList,0); + System.out.println(rob1(root)); + + } + + @Test + public void test1() { + List valList = new ArrayList<>(Arrays.asList(4,1,null,2,null,null,null,3)); + TreeNode root = TreeUtils.structureTree(valList,0); + System.out.println(rob1(root)); + + } + + + /** + * 本质上就是遍历二叉树,但是必须是后续遍历,根据后序遍历再来决定, + * 这里是错的,使用了层序遍历;误以为一层使用了一个,那么下面所有层都用不了,但实际上只有其左右节点不能用 + * @param root + * @return + */ + public int rob(TreeNode root) { + if(root==null)return 0; + if(root.left==null&&root.right==null){ + return root.val; + } + int first=root.val; + int second=0; + LinkedList queue = new LinkedList<>(); + TreeNode left = root.left; + TreeNode right = root.right; + + if(left!=null){ + second+=left.val; + if(left.left!=null)queue.addLast(left.left); + if(left.right!=null)queue.addLast(left.right); + } + if(right!=null){ + second+=right.val; + if(right.left!=null)queue.addLast(right.left); + if(right.right!=null)queue.addLast(right.right); + } + second=Math.max(first,second); + + while (!queue.isEmpty()){ + int size = queue.size(); + int add=0; + for (int i = 0; i memo = new HashMap<>(); + return robAction(root, memo); + } + + int robAction(TreeNode root, Map memo) { + if (root == null) + return 0; + if (memo.containsKey(root)) + return memo.get(root); + int money = root.val; + //计算不要下一层的 + if (root.left != null) { + money += robAction(root.left.left, memo) + robAction(root.left.right, memo); + } + if (root.right != null) { + money += robAction(root.right.left, memo) + robAction(root.right.right, memo); + } + //后面是要下一层的 + int res = Math.max(money, robAction(root.left, memo) + robAction(root.right, memo)); + memo.put(root, res); + return res; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/TreeNode.java b/Leecode/src/main/java/com/markilue/leecode/tree/TreeNode.java index aad506b..718af22 100644 --- a/Leecode/src/main/java/com/markilue/leecode/tree/TreeNode.java +++ b/Leecode/src/main/java/com/markilue/leecode/tree/TreeNode.java @@ -1,22 +1,25 @@ package com.markilue.leecode.tree; import lombok.Data; +import org.junit.Test; + +import java.util.*; @Data public class TreeNode { - int val; - TreeNode left; - TreeNode right; + public int val; + public TreeNode left; + public TreeNode right; - TreeNode() { + public TreeNode() { } - TreeNode(int val) { + public TreeNode(int val) { this.val = val; } - TreeNode(int val, TreeNode left, TreeNode right) { + public TreeNode(int val, TreeNode left, TreeNode right) { this.val = val; this.left = left; this.right = right; @@ -31,4 +34,6 @@ public class TreeNode { '}'; } + + } diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/TreeUtils.java b/Leecode/src/main/java/com/markilue/leecode/tree/TreeUtils.java new file mode 100644 index 0000000..25c3275 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/TreeUtils.java @@ -0,0 +1,51 @@ +package com.markilue.leecode.tree; + +import java.util.LinkedList; +import java.util.List; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-12-11 12:42 + * @Description: TODO 二叉树的实用工具 + * @Version: 1.0 + */ +public class TreeUtils { + + public static TreeNode structureTree(List valList, int index){ + if(index>=valList.size())return null; + TreeNode root; + Integer val = valList.get(index); + if(val!=null){ + root = new TreeNode(val); + }else { + return null; + } + //构造左子树 + root.left=structureTree(valList,2*index+1); + //构造右子树 + root.right=structureTree(valList,2*(index+1)); + return root; + } + + public static void printTreeByLevel(TreeNode root){ + LinkedList treeNodes = new LinkedList<>(); + treeNodes.addFirst(root); + while (!treeNodes.isEmpty()){ + int size = treeNodes.size(); + for (int i = 0; i < size; i++) { + TreeNode node = treeNodes.poll(); + if(node!=null){ + System.out.print(node.val+" "); + treeNodes.addLast(node.left); + treeNodes.addLast(node.right); + }else { + System.out.print("null"+" "); + } + + } + System.out.println(); + } + } +}