刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com
目录
322. 零钱兑换 - 力扣(LeetCode)
给你一个整数数组 coins
,表示不同面额的硬币;以及一个整数 amount
,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1
。
你可以认为每种硬币的数量是无限的。
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
import java.util.Arrays;
/**
* @author light
* @Description
* @create 2023-10-07 12:32
*/
public class CoinChangeTest {
public int coinChange(int[] coins, int amount) {
int[] dp=new int[amount+1];
Arrays.fill(dp,Integer.MAX_VALUE);
dp[0]=0;
for (int i = 0; i < coins.length; i++) { //先遍历物品
for (int j = coins[i]; j <=amount; j++) {
//只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要---没有的话Integer.MAX_VALUE+1会越界
if(dp[j-coins[i]]!=Integer.MAX_VALUE){
dp[j]=Math.min(dp[j],dp[j-coins[i]]+1);
}
}
}
return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];
}
}
279. 完全平方数 - 力扣(LeetCode)
给你一个整数 n
,返回 和为 n
的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1
、4
、9
和 16
都是完全平方数,而 3
和 11
不是。
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
class Solution {
//(思路:转换为完全背包问题
public int numSquares(int n) {
//dp[j]:和为j的完全平方数的最少数量为dp[j]
int[] dp=new int[n+1];
Arrays.fill(dp,Integer.MAX_VALUE);
dp[0]=0;
for (int i = 1; i*i<=n; i++) { //先遍历物品--n之内的所有完全平方数
for (int j = i*i; j <=n; j++) { //在遍历背包
if(dp[j-i*i]!=Integer.MAX_VALUE){
dp[j]=Math.min(dp[j],dp[j-i*i]+1);
}
}
}
return dp[n];
}
}
139. 单词拆分 - 力扣(LeetCode)
给你一个字符串 s
和一个字符串列表 wordDict
作为字典。请你判断是否可以利用字典中出现的单词拼接出 s
。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author light
* @Description 单词拆分
*
*
* (思路:转化为完全背包问题,字符串s--背包;字符串列表中的单词--物品
* 单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。
* 拆分时可以重复使用字典中的单词,说明就是一个完全背包
*
* @create 2023-10-07 16:25
*/
public class WordBreakTest {
public boolean wordBreak(String s, List<String> wordDict) {
//dp[j]:长度为j的字符串,dp[j]为true,表示可以利用字典中出现的单词拼接出 s
boolean[] dp=new boolean[s.length()+1];
Set<String> set=new HashSet<>(wordDict);
Arrays.fill(dp,false); //初始化
dp[0]=true;
for (int i = 0; i <s.length(); i++) { //求排列--先遍历背包
for (int j = 0; j<i; j++) { //再遍历物品
String word=s.substring(j,i); //截取字符串
if(set.contains(word)&&dp[j]==true){
dp[i]=true;
}
}
}
return dp[s.length()];
}
}
198. 打家劫舍 - 力扣(LeetCode)
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
class Solution {
public int rob(int[] nums) {
if(nums.length==1){
return nums[0];
}
//dp[i]:考虑偷(就是不一定偷)【0-i】间房,最多可以偷dp[i]的金额
int[] dp=new int[nums.length+1];
dp[0]=nums[0];
dp[1]=Math.max(nums[1],dp[0]);
for (int i = 2; i < nums.length; i++) {
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[nums.length-1];
}
}
213. 打家劫舍 II - 力扣(LeetCode)
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
/**
* @author light
* @Description 打家劫舍II
* (思路:分三种情况 情况一包含在情况二、三中
* 情况一:首尾都不考虑偷
* 情况二:考虑偷首,不考虑偷尾
* 情况三:考虑偷尾,不考虑偷首
* @create 2023-10-11 10:10
*/
public class Rob2Test {
public int rob(int[] nums) {
if(nums.length==1){
return nums[0];
}
//情况二
int res1=robAction(nums,0, nums.length-2);
//情况三
int res2=robAction(nums,1, nums.length-1);
return Math.max(res1,res2);
}
public int robAction(int[] nums, int start, int end) {
if(start==end){
return nums[start];
}
//dp[i]:考虑偷(就是不一定偷)【0-i】间房,最多可以偷dp[i]的金额
int[] dp=new int[end+1];
dp[start]=nums[start];
dp[start+1]=Math.max(dp[start],nums[start+1]);
for (int i = start+2; i <=end; i++) {
dp[i]=Math.max(dp[i-1],dp[i-2]+nums[i]);
}
return dp[end];
}
}
337. 打家劫舍 III - 力扣(LeetCode)
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root
。
除了 root
之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root
。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
/**
* @author light
* @Description 打家劫舍III
*
* (思路:这里可以使用一个长度为2的数组,记录当前节点偷与不偷所得到的的最大金钱。
*
* 树形dp---采用后序遍历二叉树,当前结点有两种情况:
* 1.考虑偷当前结点---不考虑偷当前结点的左右孩子结点
* 2.不考虑偷当前结点---取考虑偷左孩子和考虑偷右孩子的最大值之和
* @create 2023-10-11 10:59
*/
public class Rob3Test {
public int rob(TreeNode root) {
int res[]=robAction(root);
return Math.max(res[0],res[1]);
}
public int[] robAction(TreeNode root){
//dp[0]:考虑不偷当前结点所能获得的最高金额
//dp[1]:考虑偷当前结点所能获得的最高金额
int[] dp=new int[2];
if(root==null){ //每一层都要判断,递归出口
return dp;
}
int[] left=robAction(root.left);
int[] right=robAction(root.right);
//不考虑偷当前结点--取考虑偷左孩子和考虑偷右孩子的最大值之和
dp[0]= Math.max(left[0],left[1])+Math.max(right[0],right[1]);
//考虑偷当前结点---不考虑偷当前结点的左右孩子结点
dp[1]=root.val+left[0]+right[0];
return dp;
}
}