问题:
输入一组硬币值 以及需要兑换的总数 输出可兑换的最少硬币数量 不可兑换输出-1
示例:
Input: coins = [1, 2, 5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
解析:
构造一个dp数组 长度为amount+1 每个位置上存放当前amount 能够兑换的最少的硬币数量 初始dp[0]=0 其余初始值为INTEGER.MAX
递推公式为 dp[i]=Math.min(dp[i],dp[i-coins[j]+1)
关键点:
使用dp存储 每个位置上取值 dp[0]=0 是为了使当前amount 恰好等于当前coins值时 为dp[当前amount]赋值为1
对于dp[i]
当i取值从1到amount时
首先将dp[i] 赋值为Integer.MAX_VALUE
然后遍历coins 如果当前的amount大于等于当前的coins(等于的时候说明只需要当前coins一个就可以兑换)比较当前的dp[i] 和dp[amount-coins[j]]+1 的大小
代码:
class Solution {
public int coinChange(int[] coins, int amount) {
int[]dp=new int[amount+1];
dp[0]=0;
for(int i=1;i<=amount;i++){
dp[i] = Integer.MAX_VALUE;
for(int j=0;j=coins[j]&&dp[i-coins[j]]!=Integer.MAX_VALUE){
dp[i]=Math.min(dp[i],dp[i-coins[j]]+1);
}
}
}
return dp[amount]==Integer.MAX_VALUE?-1:dp[amount];
}
}
问题
输入一组硬币 以及需要兑换的总值 输出一共有多少种兑换的方式
示例
Input: amount = 5, coins = [1, 2, 5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
解析
同样适用dp数组 只不过在此使用二维数组 dp[coins+1][amount+1] 其中dp[i][j] 代表使用coins中前i种硬币 兑换 amount=j能够有多少种兑换的方法
递推公式为 dp[i][j]=dp[i-1][j]+(dp[i-1][j-amount[i-1]]+dp[i-1][j-2*amount[i-1]]+......)
仔细观察可得 dp[i-1][j-amount[i-1]]+dp[i-1][j-2*amount[i-1]]+......=dp[i][j-amount[i-1]]
代码
class Solution {
public int change(int amount, int[] coins) {
int[][] dp=new int[coins.length+1][amount+1];
dp[0][0]=1;
for(int i=1;i<=coins.length;i++){
dp[i][0]=1;
for(int j=1;j<=amount;j++){
dp[i][j]=dp[i-1][j]+(j-coins[i-1]>=0?dp[i][j-coins[i-1]]:0);
}
}
return dp[coins.length][amount];
}
}
仔细观察上述表达式 在递归的过程中只和 dp[i-1] [j] 以及dp[i][j-coins[i-1]]有关 其实可以压缩为一维数组
class Solution {
public int change(int amount, int[] coins) {
int []dp=new int[amount+1];
dp[0]=1;
for(int coin:coins){
for(int j=coin;j<=amount;j++){
dp[j]=dp[j]+dp[j-coin]
}
}
return dp[amount];
}
}
问题:
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path
示例:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
解析:
使用dp[][]数组 初始化dp[0][i] =dp[0][i-1]+grid[0][i] 初始化 dp[j][0] =dp[j-1]+grid[j][0]
递推公式 dp[i][j]=Math.min(dp[i-1][j],dp[i],[j-1])+grid[i][j]
因为dp[i-1][j] 在一维数组中可以用自身表示 所以可以 将其简化为一维数组 dp[i]=Math.min(dp[i],dp[i-1])+grid[i][j]
代码:
class Solution {
public int minPathSum(int[][] grid) {
// int row= grid.length;
// if(row==0){
// return 0;
// }
// int col=grid[0].length;
// int[][]dp=new int[row][col];
// dp[0][0]=grid[0][0];
// for(int i=1;i
问题:
对于一个m行 n列的矩阵 只能向下或者向右走 总共有多少种不同的走法到达右下角
解析
此题和最短路径和是一摸一样的解法 只是在初始化第一行的时候初始值全部为1
代码:
class Solution {
public int uniquePaths(int m, int n) {
// int[][]dp= new int[m][n];
// for(int i=0;i
Unique Path 2 解析
对于Unique Path 2 主要的区别是增加了一个阻碍矩阵 某些位置不可通过
这样使得初始化的时候不仅要观察阻碍矩阵对应位置是否可通过 同时还要确保该位置的前一位置可通过
代码
class Solution {
public int uniquePathsWithObstacles(int[][] obstacleGrid) {
// int m=obstacleGrid.length;
// int n=obstacleGrid[0].length;
// int[][] dp=new int[m][n];
// dp[0][0]=obstacleGrid[0][0]==1?0:1;
// for(int i=1;i
问题:
在一个数组中寻找最长的递增子序列
示例:
Input: [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
解析1
可以创建一个数组int[]result=new int[nums.length] 以每一个元素i分别作为比较对象
遍历其之前的所有元素 如果元素i大于元素j 则表示其可以作为元素j之后的递增序列的一员 因此在元素j的基础上可以长度加1 同时与当前长度比较取较长的一个.
代码:
if(nums.length==0){
return 0;
}
int[]result=new int[nums.length];
int max=1;
for(int i=0;inums[j]){
result[i]=Math.max(result[i],result[j]+1);
max=Math.max(max,result[i]);
}
}
}
return max;
解析2:
以[10,9,2,5,3,7,101,18] 为例 遍历数组 在结果数组中寻找第一个比该值大的元素并替换
可以使用二分查找binarySearch(int[] nums,int target)寻找一个数组中第一个比target大的位置
代码
public int lengthOfLIS(int[] nums) {
int []result=new int[nums.length];
for(int i=0;i=0;j--){
if(result[j]!=Integer.MAX_VALUE){
return j+1;
}
}
return 0;
}
private int binarySearch(int[] nums,int target){
int begin=0;
int end=nums.length-1;
while(begintarget){
end=mid;
}else{
return mid;
}
}
return end;
}
对于回溯方法而言 ,最典型的应用场景是不易顺次的通过多重循环得到最终的结果(因为很难确定内层循环的次数),此时可以通过回溯的方法 首先确定外层的一个取值 然后逐层推进
例如
Letter Combinations of a Phone Number
每个按键代表若干个字母 给定若干个按键 确定所有字母的组合
此时最先想到的就是逐层遍历 但是因为层数不确定 所以无法穷尽循环,可以回溯的方法 对于每一层的操作都是相同的 即 给定前一层的结果以及该层所需要遍历的内容 然后进行拼接的操作 如果到达终止条件返回 如果不到达终止条件 则继续递归下去
class Solution {
Map phone = new HashMap() {{
put("2", "abc");
put("3", "def");
put("4", "ghi");
put("5", "jkl");
put("6", "mno");
put("7", "pqrs");
put("8", "tuv");
put("9", "wxyz");
}};
List result =new ArrayList<>();
public void backUpon(String pre,String cur){
if(cur.length()==0){
result.add(pre);
}else{
String nOperate=cur.substring(0,1);
String nTarget=phone.get(nOperate);
for(int i=0;i letterCombinations(String digits) {
if(digits.length()!=0){
backUpon("",digits);
}
return result;
}
}