leecode更新
This commit is contained in:
parent
5f1ccb4541
commit
620b260e4a
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Integer> 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<Integer> 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<TreeNode> 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 <size; i++) {
|
||||||
|
TreeNode node = queue.poll();
|
||||||
|
add+=node.val;
|
||||||
|
if(node.left!=null)queue.addLast(node.left);
|
||||||
|
if(node.right!=null)queue.addLast(node.right);
|
||||||
|
}
|
||||||
|
int temp=second;
|
||||||
|
second=Math.max(second,first+add);
|
||||||
|
first=temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return second;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态规划法尝试
|
||||||
|
* 速度击败100%,内存击败31%
|
||||||
|
* @param root
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int rob1(TreeNode root) {
|
||||||
|
int[] dfs = dfs(root);
|
||||||
|
return Math.max(dfs[0],dfs[1]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 偷不偷当前节点 {偷当前节点1,不偷当前节点}
|
||||||
|
* @param node
|
||||||
|
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private int[] dfs(TreeNode node){
|
||||||
|
if(node==null) return new int[2];
|
||||||
|
int[] leftSteal = dfs(node.left);
|
||||||
|
int[] rightSteal = dfs(node.right);
|
||||||
|
//偷当前节点:不偷左边+不偷右边+偷当前
|
||||||
|
int steal = node.val + leftSteal[1]+ rightSteal[1];
|
||||||
|
//不偷当前节点:偷不偷左边最大的+偷不偷右边最大的
|
||||||
|
int noSteal = Math.max(leftSteal[1],leftSteal[0])+Math.max(rightSteal[1],rightSteal[0]);
|
||||||
|
return new int[]{steal,noSteal};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 代码随想录,记忆搜索递归法
|
||||||
|
* 速度击败39.67%,内存击败6.1%
|
||||||
|
* @param root
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
// 2.递归去偷,记录状态
|
||||||
|
// 执行用时:3 ms , 在所有 Java 提交中击败了 56.24% 的用户
|
||||||
|
public int rob2(TreeNode root) {
|
||||||
|
Map<TreeNode, Integer> memo = new HashMap<>();
|
||||||
|
return robAction(root, memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
int robAction(TreeNode root, Map<TreeNode, Integer> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
package com.markilue.leecode.tree;
|
package com.markilue.leecode.tree;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class TreeNode {
|
public class TreeNode {
|
||||||
|
|
||||||
int val;
|
public int val;
|
||||||
TreeNode left;
|
public TreeNode left;
|
||||||
TreeNode right;
|
public TreeNode right;
|
||||||
|
|
||||||
TreeNode() {
|
public TreeNode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode(int val) {
|
public TreeNode(int val) {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeNode(int val, TreeNode left, TreeNode right) {
|
public TreeNode(int val, TreeNode left, TreeNode right) {
|
||||||
this.val = val;
|
this.val = val;
|
||||||
this.left = left;
|
this.left = left;
|
||||||
this.right = right;
|
this.right = right;
|
||||||
|
|
@ -31,4 +34,6 @@ public class TreeNode {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<Integer> 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<TreeNode> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue