LeetCode动态规划经典题(二)

70. 爬楼梯

https://leetcode.cn/problems/climbing-stairs/
在这里插入图片描述

思路:对于3阶及以上的台阶n, 有两种选择可以到达台阶n, 一是从n-1级爬1阶到达,二是从n-2级爬2阶到达, 因此dp[n]=dp[n-1]+dp[n-2], 为了减小空间复杂度,可以使用两个变量代替dp数组

class Solution {
    public int climbStairs(int n) {
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        int dp1=1;
        int dp2=2;
        for(int i=3;i<=n;i++){
            int tmp=dp2;
            dp2=dp1+dp2;
            dp1=tmp;
        }
        return dp2;
    }
}
//O(n)
//O(1)

198. 打家劫舍

https://leetcode.cn/problems/house-robber/
在这里插入图片描述

思路:
dp[i][0]: 表示第i间房子没有被偷可以获得的最大利润
dp[i][1]: 表示第i间房子被偷可以获得的最大利润

dp[i][0]=max(dp[i-1][0],dp[i-1][1]: 第i间房子没有被偷可以有两个状态转移过来:1. 第i-1间房子被偷 2. 第i-1间房子没有被偷

dp[i][1]=max(dp[i-1][0]+nums[i],dp[i-1][1]: 第i间房子被偷可以有两个状态转移过来:1. 第i-1间房子没有被偷,此时加上当前房屋的金额 2. 第i-1间房子没有被偷

class Solution {
    public int rob(int[] nums) {
        int n=nums.length;
        int[][] dp=new int[n][2];
        dp[0][0]=0;
        dp[0][1]=nums[0];
        for(int i=1;i<n;i++){
            dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]);
            dp[i][1]=Math.max(dp[i-1][0]+nums[i],dp[i-1][1]);
        }
        return Math.max(dp[n-1][0],dp[n-1][1]);
        
    }
}
//O(n)
//O(n)

可以使用变量来代替dp数组,使得空间复杂度为O(1)

class Solution {
    public int rob(int[] nums) {
        int n=nums.length;
        int a=0;
        int b=nums[0];
        for(int i=1;i<n;i++){
            int tmpA=a;
            a=Math.max(a,b);
            b=Math.max(tmpA+nums[i],b);
        }
        return Math.max(a,b);
        
    }
}
//O(n)
//O(1)

213. 打家劫舍 II

https://leetcode.cn/problems/house-robber-ii/
在这里插入图片描述

思路:第一间房子和最后一间房子是相邻的,因此两间房子只能偷一间。第一个和最后一个不能同时抢。 所以:要么不抢第一个,要么不抢最后一个。 注意,不抢第一个的时候,最后一个可抢可不抢;另一种情况同理,因此可以看成两个问题,在区间[0,n-2]内的最大金额和区间[1,n-1]最大金额(转化为198. 打家劫舍中的问题),取二者中的最大值即可

class Solution {
    public int rob(int[] nums) {
        int n=nums.length;
        if(n==0){
            return 0;
        }
        if(n==1){
            return nums[0];
        }
        //Arrays.copyOfRange 这里包括下标from,不包括上标to。[from,to)
        int maxVal1=rob2(Arrays.copyOfRange(nums, 0, n - 1));//[0,n-1]区间的房子
        int maxVal2=rob2(Arrays.copyOfRange(nums, 1, n));[1,n]区间的房子
        return Math.max(maxVal1,maxVal2);

    }
//下面的代码分析见198. 打家劫舍
    public int rob2(int[] nums) {
        int n=nums.length;
        int a=0;
        int b=nums[0];
        for(int i=1;i<n;i++){
            int tmpA=a;
            a=Math.max(a,b);
            b=Math.max(tmpA+nums[i],b);
        }
        return Math.max(a,b);
        
    }
}
//O(n)
//O(1)

337. 打家劫舍 III

https://leetcode.cn/problems/house-robber-iii/
在这里插入图片描述

思路:记作f(o)为选取了以节点o作为根节点的子树中可以获取的最大价值,g(o)为没选取了以节点o作为根节点的子树中可以获取的最大价值,


f(o)=o.value+g(o.left)+g(o.right): 选择了节点o, o的左右子节点就不能再被选择了
g(o)=max(f(o.left),g(o.left))+max(f(o.right),g(o.right)): 选择了节点o, o的左右子节点可以被选择也可以不被选择,取其中的最大值相加

class Solution {
    HashMap<TreeNode,Integer> f=new HashMap<>();
    HashMap<TreeNode,Integer> g=new HashMap<>();
    public int rob(TreeNode root) {
        dfs(root);
        return Math.max(f.getOrDefault(root,0),g.getOrDefault(root,0));
    }
    public void dfs(TreeNode node){
        if(node==null){
            return;
        }
        dfs(node.left);
        dfs(node.right);
        f.put(node,node.val+g.getOrDefault(node.left,0)+g.getOrDefault(node.right,0));
        int leftMax=Math.max(f.getOrDefault(node.left,0),g.getOrDefault(node.left,0));
        int rightMax=Math.max(f.getOrDefault(node.right,0),g.getOrDefault(node.right,0));
        g.put(node,leftMax+rightMax);
    }
}

可以使用一个大小为2的数组返回来代替哈希表,减小空间的开销和操作的时间

class Solution {
    public int rob(TreeNode root) {
        int[] ans=dfs(root);
        return Math.max(ans[0],ans[1]);
    }
    public int[] dfs(TreeNode node){
        if(node==null){
            return new int[]{0,0};
        }
        int[] leftVal=dfs(node.left);
        int[] rightVal=dfs(node.right);
        int selected=node.val+leftVal[1]+rightVal[1];
        int notSelected=Math.max(leftVal[0],leftVal[1])+Math.max(rightVal[0],rightVal[1]);
        return new int[]{selected,notSelected}; 
    }
}
//O(n)
//o(n)

118. 杨辉三角

https://leetcode.cn/problems/pascals-triangle/
在这里插入图片描述

思路:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

下一行的第一个元素和最后一个元素是1,其他元素dp[i][j]=dp[i-1][j-1]+dp[i-1][j] dp[i][j]表示第i行的第j个元素,不是第一个元素的话,该元素等于上一行同列的元素和上一行左边一列的元素之和

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> ans=new ArrayList<>();
        for(int i=0;i<numRows;i++){//[0,numsRows-1] 一共numRows行
            List<Integer> tmp=new ArrayList<>();
            for(int j=0;j<=i;j++){//第i行有i+1个元素(i从0开始) [0,i]
                if(j==0||j==i){//第一个元素和最后一个元素
                    tmp.add(1);
                }else{
                    //dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
                    tmp.add(ans.get(i-1).get(j-1)+ans.get(i-1).get(j));
                }
            }
            ans.add(tmp);
        }
        return ans;
    }
}
//O(numsRows^2)
//O(1) 返回值不计入空间

119. 杨辉三角 II

https://leetcode.cn/problems/pascals-triangle-ii/
在这里插入图片描述

思路:和118. 杨辉三角思路相同,118是给定numRows, 生成numRows行的数据,本题给出rowIndex, 实际上是要生成roeIndex+1行数据,返回第roeIndex+1行即可

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<List<Integer>> ans=new ArrayList<>();
        for(int i=0;i<=rowIndex;i++){//[0,rowIndex] 一共rowIndex+1行
            List<Integer> tmp=new ArrayList<>();
            for(int j=0;j<=i;j++){//第i行有i+1个元素(i从0开始) [0,i]
                if(j==0||j==i){//第一个元素和最后一个元素
                    tmp.add(1);
                }else{
                    //dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
                    tmp.add(ans.get(i-1).get(j-1)+ans.get(i-1).get(j));
                }
            }
            ans.add(tmp);
        }
        return ans.get(rowIndex);
    }
   
}
//O(rowIndex^2)
//O(1)

1143. 最长公共子序列

[https://leetcode.cn/problems/longest-common-subsequence/](https://leetcode.cn/problems/longest-common-subsequence/
在这里插入图片描述

思路:记作dp[i][j]为text1[0:i-1]和text2[0:j-1]区间内的最长公共子序列的长度,当i=0或j=0时表示一方是空串,这是base情况,此时dp[0]j]dp[i][0] 的值为0, 当i>=1和j>=1时,如果text1[i]=text2[j], 说明公共部分长度可以加1,不相等的话,取dp[i][j-1]和dp[i-1][j]中的较大值

在这里插入图片描述

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m=text1.length(),n=text2.length();
        int[][] dp=new int[m+1][n+1];
        //dp[i][j]表示text1[0:i-1]  text1[0:j-1] 的LCS长度
        //当i=0时  dp[0][j]=0 表示当text1为空串时 LCS长度为0
        //当j=0时  dp[i][0]=0 表示当text2为空串时 LCS长度为0
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                if(text1.charAt(i-1)==text2.charAt(j-1)){
                    dp[i][j]=dp[i-1][j-1]+1;
                }else if(dp[i-1][j]>dp[i][j-1]){
                    dp[i][j]=dp[i-1][j];
                }else{
                     dp[i][j]=dp[i][j-1];
                }
            }
        }
        return dp[m][n];
    }
}
//O(mn)
//O(mn)

221. 最大正方形

https://leetcode.cn/problems/maximal-square/
在这里插入图片描述

思路1:暴力法,当遇到某个位置(i,j)是1时,判断以该点为正方形左上角点的正方形的最大边长,记为k, 然后判断以左上角(i,j)—>右下角(i+k-1,j+k-1)区间内的元素是否都是1, 在判断的时候我们可以同时判断某一行和某一列,而不是使用二重for循环取逐个元素进行判断

class Solution {
    public int maximalSquare(char[][] matrix) {
        int maxEdge=0;
        if(matrix==null||matrix.length==0||matrix[0].length==0){
            return maxEdge;
        }
        int m=matrix.length,n=matrix[0].length;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]=='1'){
                    int limit=Math.min(m-i,n-j);
                    for(int k=1;k<=limit;k++){
                        boolean flag=true;
                        for(int h=0;h<k;h++){
                        //matrix[i+k-1][j+h]=='0'  判断第k-1行是否都是1
                        //matrix[i+h][j+k-1]=='0'  判断第k-1列是否都是1
                            if(matrix[i+k-1][j+h]=='0'||matrix[i+h][j+k-1]=='0'){
                                flag=false;
                                break;
                            }
                        }
                       if(flag){
                           maxEdge=Math.max(maxEdge,k);
                       }else{
                           break;
                       }
                    }    
                }
            }
        }
        return maxEdge*maxEdge;
    }
}
//O(mn*min(m,n)^2)
//O(1)

思路2: 动态规划,用dp[i][j]表示以(i,j)为右下角的满足正方形的最大边长
在这里插入图片描述

class Solution {
    public int maximalSquare(char[][] matrix) {
        int maxEdge=0;
        if(matrix==null||matrix.length==0||matrix[0].length==0){
            return maxEdge;
        }
        int m=matrix.length,n=matrix[0].length;
        int[][] dp=new int[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(matrix[i][j]=='1'){
                    if(i==0||j==0){
                        dp[i][j]=1;
                    }else{
                        dp[i][j]=Math.min(Math.min(dp[i-1][j],dp[i][j-1]),dp[i-1][j-1])+1;
                    }
                }
                maxEdge=Math.max(maxEdge,dp[i][j]);
            }
        }
        return maxEdge*maxEdge;
    }
}
//O(mn)
//O(mn)

300. 最长递增子序列

https://leetcode.cn/problems/longest-increasing-subsequence/
在这里插入图片描述

思路:
记作dp[i]为以nums[i]结尾的的最长上升子序列的长度,在计算dp[i]之前,dp[0]---dp[i-1]已经被计算过,j在区间[0–i-1]内,如果nums[i]>nums[j], 则元素nums[i]可以连在以nums[j]结尾的子序列后面,此时长度加一,得到一个更长的上升子序列

之后统计以每个nums[i]结尾的上升子序列的长度,取最大值

dp[i]=max(dp[i],dp[j]+1) 0<=j<i&nums[i]>nums[j]

class Solution {
    public int lengthOfLIS(int[] nums) {
        int n=nums.length;
        int maxLen=0;
        int[] dp=new int[n];
        for(int i=0;i<n;i++){
            dp[i]=1;//单个数字形成的序列长度为1
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            maxLen=Math.max(maxLen,dp[i]);
        }
        return maxLen;
    }
}
//O(n^2)
//O(n)

873. 最长的斐波那契子序列的长度

https://leetcode.cn/problems/length-of-longest-fibonacci-subsequence/
在这里插入图片描述

思路:

记作dp[i][j] i<j 表示以arr[i] arr[j]结尾的斐波那契数列的长度,如果在区间[0,i-1]内有k满足arr[k]+arr[i]=arr[j] 则以arr[i] arr[j]结尾的斐波那契数列的长度可以在arr[k] arr[i]结尾的斐波那契数列的长度的基础上加一

因此dp[i][j]=max(dp[k][i]+1,3) 0<=k<i 这里和3比较是因为当dp[k][i]<3时,arr[k] arr[j]结尾的数列虽然不是斐波那契数列,但是加上arr[j]就是一个斐波那契数列了。 另外注意一个细节,因为数组是递增的,当arr[i]+arr[i]<=arr[j]时,可以停止j的往后搜索了,因为不可能找到一个k, 使得arr[k]+arr[i]=arr[j]

class Solution {
    public int lenLongestFibSubseq(int[] arr) {
        int n=arr.length;
        int[][] dp=new int[n][n];
        HashMap<Integer,Integer> cnt=new HashMap<>();
        for(int i=0;i<n;i++){
            cnt.put(arr[i],i);
        }
        int maxLen=0;
        for(int i=1;i<n-1;i++){//i取值[1,n-2]
            for(int j=i+1;j<n;j++){
                if(2*arr[i]<=arr[j]){//arr[index]<arr[i] 如果arr[i]+arr[i]<=arr[j]
                //一定有arr[index]+arr[i]<arr[j]  此时不需要往后寻找了 不会有满足条件的j 提前结束内循环
                    break;
                }
                int index=cnt.getOrDefault(arr[j]-arr[i],-1);
                //index>=0 说明存在arr[j]-arr[i]=arr[index]-->arr[j]=arr[index]+arr[i]
                //index<i  因为要求arr[index]+arr[i]=arr[j]  index<i<j
                if(index>=0&&index<i){
                   //若dp[index][i]<3--->dp[i][j]=3
                   //若dp[index][i]>=3--->dp[i][j]=dp[index][i]+1
                    dp[i][j]=Math.max(3,dp[index][i]+1);//这里不需要dp[i][j]=max(dp[i][j],xxx)
                    //因为dp[i][j]只会出现一次
                }
                maxLen=Math.max(maxLen,dp[i][j]);
            }  
        }
        return maxLen;
    }
}
//O(n^2)
//O(n^2)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodePanda@GPF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值