leetcode 动态规划——java实现

14 篇文章 0 订阅
14 篇文章 0 订阅

严格递增:后面必须比前面的大
非严格递增:可以相等

300. 最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的
子序列。
示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

以前做这题的经典解法:(但耗时长)

class Solution {
    public int lengthOfLIS(int[] nums) {
        int[] dp=new int[2550];//dp[i]表示i点为结尾(在序列内)的最长上升子序列数量
        int maxx=-1,n=nums.length;
        //那么每次只要比较当前点能连上前面的哪个dp
        for(int i=0;i<n;i++){
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    int t=dp[j]+1;
                    dp[i]=Math.max(t,dp[i]);//取最大值
                }
            }
            maxx=Math.max(maxx,dp[i]);
        }
        return maxx;
    }
}

上述方法,标准解释:
状态转移方程为:

dp[i]=max(dp[j])+1,其中0≤j<i且num[j]<num[i]

1143. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:

输入:text1 = “abcde”, text2 = “ace”
输出:3
解释:最长公共子序列是 “ace” ,它的长度为 3 。
示例 2:

输入:text1 = “abc”, text2 = “abc”
输出:3
解释:最长公共子序列是 “abc” ,它的长度为 3 。
示例 3:

输入:text1 = “abc”, text2 = “def”
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:
1 <= text1.length, text2.length <= 1000
text1 和 text2 仅由小写英文字符组成。

状态转移方程:

若a[i]=b[j], 则 dp[i][j]=dp[i-1][j-1] +1

若a[i]!=b[j], 则 L[i][j]=max (dp[i][j-1],dp[i-1][j])

还有其他降低时间复杂度或空间复杂度的方法见 详解最长公共子序列问题(三种方法)

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int s1=text1.length(),s2=text2.length();
        int[][] dp=new int[s1+1][s2+1];//s1长度为i,s2长度为j时最长公共子序列的长度
        dp[0][0]=0;
        for(int i=1;i<=s1;i++){
            for(int j=1;j<=s2;j++){
                if(text1.charAt(i-1)==text2.charAt(j-1)){//s1[i]==s2[j]
                    dp[i][j]=dp[i-1][j-1]+1;
                }
                else{
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
                }
            }
        }
        return dp[s1][s2];

    }
}

1218. 最长定差子序列

给你一个整数数组 arr 和一个整数 difference,请你找出并返回 arr 中最长等差子序列的长度,该子序列中相邻元素之间的差等于 difference 。
子序列 是指在不改变其余元素顺序的情况下,通过删除一些元素或不删除任何元素而从 arr 派生出来的序列。

示例 1:

输入:arr = [1,2,3,4], difference = 1
输出:4
解释:最长的等差子序列是 [1,2,3,4]。
示例 2:

输入:arr = [1,3,5,7], difference = 1
输出:1
解释:最长的等差子序列是任意单个元素。
示例 3:

输入:arr = [1,5,7,8,5,3,4,2,1], difference = -2
输出:4
解释:最长的等差子序列是 [7,5,3,1]。

提示:

1 <= arr.length <= 105
-104 <= arr[i], difference <= 104

与我以前做的这题类似连续数字的最长上升子序列
本质:在哈希表里做dp
本质:用哈系桶装着出现的数,dp[x]表示x为底最长子序列长度

class Solution {
    public int longestSubsequence(int[] arr, int difference) {
        int n=arr.length;
        int maxx=-1;
        int[] dp=new int[10005];//哈希桶,下标可能出现负数不能用数组了,改用hashmap
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i=0;i<n;i++){
        //主打的是用哈希表方便查询
            if(map.containsKey(arr[i]-difference)){//间隔diffenence的值是否存在
                 map.put(arr[i],map.get(arr[i]-difference)+1);
            }
            else map.put(arr[i],1);
            //前一个值没出现过则为1,出现了就在前一个值的基础上+1
            maxx=Math.max(maxx,map.get(arr[i]));
        }
        return maxx;

    }
}

70. 爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
示例 1:

输入:n = 2
输出:2
解释:有两种方法可以爬到楼顶。

  1. 1 阶 + 1 阶
  2. 2 阶
    示例 2:

输入:n = 3
输出:3
解释:有三种方法可以爬到楼顶。

  1. 1 阶 + 1 阶 + 1 阶
  2. 1 阶 + 2 阶
  3. 2 阶 + 1 阶

关键:因为每次只能爬 1 级或 2 级,所以 f(x) 只能从 f(x−1) 和 f(x−2) 转移过来,而这里要统计方案总数,我们就需要对这两项的贡献求和。

class Solution {
    public int climbStairs(int n) {
        int[] dp=new int[50];//
        dp[1]=1;dp[2]=2;
        for(int i=3;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

746. 使用最小花费爬楼梯

给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。

你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。

请你计算并返回达到楼梯顶部的最低花费。

示例 1:

输入:cost = [10,15,20]
输出:15
解释:你将从下标为 1 的台阶开始。

  • 支付 15 ,向上爬两个台阶,到达楼梯顶部。
    总花费为 15 。
    示例 2:

输入:cost = [1,100,1,1,1,100,1,1,100,1]
输出:6
解释:你将从下标为 0 的台阶开始。

  • 支付 1 ,向上爬两个台阶,到达下标为 2 的台阶。
  • 支付 1 ,向上爬两个台阶,到达下标为 4 的台阶。
  • 支付 1 ,向上爬两个台阶,到达下标为 6 的台阶。
  • 支付 1 ,向上爬一个台阶,到达下标为 7 的台阶。
  • 支付 1 ,向上爬两个台阶,到达下标为 9 的台阶。
  • 支付 1 ,向上爬一个台阶,到达楼梯顶部。
    总花费为 6 。

hint:示例应该改成[20,15,20,0]这种更好理解吧。
本质状态转移方程:dp[i] = min( dp[i - 1], dp[i - 2] ) + cost[i];

与上题爬楼梯类似,只是增加了每层的值,求和最小即可,关键在于示例1
在这里插入图片描述

所以将顶设值为0更合适,以下算法就是将cost[n]的时候取值为0.

class Solution {
    public int minCostClimbingStairs(int[] cost) {
        int n=cost.length;
        int[] dp = new int[n+1];
        dp[0]=cost[0];dp[1]=cost[1];
        for(int i=2;i<=n;i++){
            if(i==n){
                dp[i]=Math.min(dp[i-1],dp[i-2])+0;
            }
            else{
                dp[i]=Math.min(dp[i-1],dp[i-2])+cost[i];
            }
        }
        return dp[n];
    }
}

62. 不同路径

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
输入:m = 3, n = 7
输出:28

示例 2:
输入:m = 3, n = 2
输出:3

示例 3:
输入:m = 7, n = 3
输出:28

示例 4:
输入:m = 3, n = 3
输出:6

class Solution {
    public int uniquePaths(int m, int n) {
        //对于每个位置i,j有两种来的方式,从左边从上边来
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(i==0||j==0) dp[i][j]=1;
                // else if(i==0&j) dp[i][j]=dp[i][j-1];
                // else if(j==0) dp[i][j]=dp[i-1][j];
                else{
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
        }
        return dp[m-1][n-1];

    }
}

64. 最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。

示例 1:
输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:
输入:grid = [[1,2,3],[4,5,6]]
输出:12

和上题类似的走法,上题是用dp记录路径数,这题是dp记录到以i,j为结尾的路径和

class Solution {
    public int minPathSum(int[][] grid) {
        int m=grid.length,n=grid[0].length;
        int[][] dp = new int[m][n];//从头到i,j点的最短路径长度
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(i==0&&j==0) dp[i][j]=grid[0][0];
                else if(i==0&&j>0) dp[i][j]=dp[i][j-1]+grid[i][j];
                else if(j==0&&i>0) dp[i][j]=dp[i-1][j]+grid[i][j];
                else{
                    dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i][j];
                }
            }
        }
        return dp[m-1][n-1];
    }
}

118. 杨辉三角

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:

输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:

输入: numRows = 1
输出: [[1]]

class Solution {
    public List<List<Integer>> generate(int numRows) {
        //这题说是简单,感觉难在list列表的使用上
        
        List<List<Integer>> list = new ArrayList<List<Integer>>();  
        for(int i=0;i<numRows;i++){
            List<Integer> row = new ArrayList<Integer>();
            for(int j=0;j<=i;j++){
                if(j==0||j==i) row.add(1);
                else{
                    System.out.println(i);
                    row.add(list.get(i-1).get(j-1)+list.get(i-1).get(j));
                } 
            }
            list.add(row);
        }
        return list;         
        
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值