面试准备------动态规划问题

动态规划

1. Coin Change

问题:

输入一组硬币值 以及需要兑换的总数 输出可兑换的最少硬币数量 不可兑换输出-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];
    }
}

2. Coin Change 2

问题

输入一组硬币 以及需要兑换的总值  输出一共有多少种兑换的方式

示例

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];
    }
    }

3.最小路径和

问题:

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

4.Unique Path & Unique Path 2

问题:

对于一个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

5. 最长递增子序列

问题:

在一个数组中寻找最长的递增子序列

示例:

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;
        
 
    }

6.最长公共子序列

7.回溯法

对于回溯方法而言 ,最典型的应用场景是不易顺次的通过多重循环得到最终的结果(因为很难确定内层循环的次数),此时可以通过回溯的方法 首先确定外层的一个取值 然后逐层推进
例如

 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;
        
      
    }
}

你可能感兴趣的:(面试问题总结)