博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Leetcode上动态规划系列经典题的Java详解
阅读量:4112 次
发布时间:2019-05-25

本文共 4724 字,大约阅读时间需要 15 分钟。

宅家防疫期间leetcode上小刷了十余道线性动态规划算法题,是时候自己总结提炼一下DP思想了。

我把总结的题目主要分为两类序列匹配类和生活类。
第一类的代表热题:

第二类的代表热题:

以下为7道题的思路和代码实现

300.最长上升子序列在这里插入图片描述

  • 思路 :用一个数组来存放上升序列,关键在于最后输出的是序列长度,所以保证长度而不需要时刻记录真正的子序。
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length==0) return 0; for(int i=1; i
1; j--){
if(dp[j] < dp[i]) dp[j] = dp[j-1]+1; } } }}

之所以最先写这道题,因为之前用的是贪心算法,二分搜索比较难想到

public static int lengthOfLIS(int[]nums) {
if(nums.length < 2) return nums.length; //LIS数组存放最长子序列的数组,但并非时刻都存放的是最长子序列 int[] LIS = new int[nums.length]; LIS[0] = nums[0];//数组有负整数的情况 int end = 0; for(int i=1; i
LIS[end]){
end++; LIS[end]=nums[i]; } //如果当前nums[i]小于子序列最后一位,则用二分法搜索子序列中比nums[i]大的最小数 else{
int left = 0,right =end; while(left
= nums[i]; right = pivot; } } LIS[right]=nums[i]; } } return end+1; }

LeetCode53. 最大子序和

  • 思路:子序列问题难点在于是不连续的序列。定义dp[i] 表⽰以 nums[i] 这个数结尾的最⻓递增⼦序列的⻓度。dp[i]依赖于dp[i-1]的结果,每次递推都要max记录,自底向上推算出最后结果啦。先用一个简单的例子来推:nums = [1,-2,3],则dp[0] = nums[0] = 1;
  • step1:dp[1] = dp[0]+nums[1] = -1; 此时max =1>dp[1],所以当前max不变;
  • step2: dp[ 2] = dp[1] + nums[2] = 1;此时 max<dp[2],所以max更新为dp[2]
  • 如此这般,max最后就是最大的dp[i]啦
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length]; dp[0] = nums[0]; int max = nums[0]; for (int i = 1; i < nums.length; i++) {
//nums[i] > 0,说明对结果有增益,dp[i]再加当前遍历值 //nums[i] <= 0,说明对结果无增益,dp[i]直接更新为当前遍历数字 dp[i] = Math.max(dp[i- 1] + nums[i], nums[i]); if (max < dp[i]) {
//关键步:取每次遍历的当前最大和 max = dp[i]; } } return max; }

LeetCode1143. 最长公共子序列

在这里插入图片描述在这里插入图片描述

  • 思路:
    这里推荐youtube上一个白板演示算法思路的小哥,他的视频讲解的非常细,既可以帮助透彻理解,又可以提高下英语,两全其美:
    中间状态都存在二维数组中,dp[i][j] 表示 串1的前 i 位和串2的前 j 位的位置。
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int n1 = text1.length(); int n2 = text2.length(); int[][] dp = new int[n1+1][n2+1]; for(int i=0; i

LeetCode72. 编辑距离

题目

  • 思路:这类解决两个字符串的动态规划问题,可以用最长上升子序列一样的方法,用两个指针 i,j 分别指向两个字符串的最后,然后一步步往前走,缩小问题的规模。
    dp[i][j]—word1的前 i 位转换成 word2的前 j 位需要的最小步数
public int minDistance(String word1, String word2) {
int n1 = word1.length(); int n2 = word2.length(); int[][] dp = new int[n1+1][n2+1]; for(int i=1; i<=n1; i++) dp[i][0] = dp[i-1][0] +1; for(int j=1; j<=n2; j++) dp[0][j] = dp[0][j-1] +1; for(int i=1; i<=n1; i++){
for(int j=1; j<=n2; j++){
//word1和word2的该位字符相同,不需要改动。 if(word1.charAt(i-1)==word2.charAt(j-1)) dp[i][j] = dp[i-1][j-1]; //如果字符不同,则取该步之前的状态基础上做删除,修改,插入中的最小改动 else dp[i][j] = Math.min(Math.min(dp[i][j-1],dp[i-1][j]),dp[i-1][j-1])+1; } } return dp[n1][n2]; }

股票系列的问题推荐一个总结很到位的思路:

在这里插入图片描述

  • 思路:前 i 天所获利润以来于前 i- 天利润;第 i 天是如果 “买入股票”,就要从利润中减去 prices[i],如果“卖出股票”,就要给利润增加 prices[i]。此时最大利润就是这两种可能选择中较大的那个。
    因此可有最优子结构:
    dp[i][0]表示第i天不持股票时拥有的利润;dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]);
    而dp[i][1]表示第i天持有股票所获利润 dp[i][1] = Math.max(dp[i-1][1], -prices[i]);
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length; if(prices.length==0)return 0; int[][] dp = new int[n][2]; //行表示第 i天,列表示是否买入当天股票 dp[0][0] = 0; //i = 0 时 dp[i-1] 是不合法的。 dp[0][1] = -prices[0]; for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i]); dp[i][1] = Math.max(dp[i-1][1], -prices[i]); } return dp[n - 1][0]; }}

在这里插入图片描述

+思路:和上一题的思路基本一致,区别在于交易次数从 1 变成 无限次
那么dp[i][1] 就等于dp[i-1][1]和dp[i-1][0]-prices[i]中更大的结果。因为前 i 天也可以有买入交易了,也就是买股票的第 i天所获利润和第 i-1 直接相关。

class Solution {
public int maxProfit(int[] prices) {
if(prices.length==0) return 0; int[][] dp = new int[prices.length][2]; dp[0][0] = 0; dp[0][1] = -prices[0]; for(int i=1; i

leetcode322. 零钱兑换

在这里插入图片描述

  • 思路:最优化状态dp[i]表示金额 i 需要的最少硬币个数。要注意的问题在于初始化时每个dp[]数都要大于金额总数,是极端假设硬币都是 1的情况。
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount+1]; // 注意:因为要比较的是最小值,不可能是结果的初始化值就得赋一个最大值 Arrays.fill(dp, amount + 1); dp[0] =0; for(int i=1; i<=amount; i++){
for(int coin: coins){
//如果可包含coin,那么剩余钱是i−coins,要兑换的硬币数是 dp[i−coins]+1 if(coin <= i) dp[i] = Math.min(dp[i],dp[i-coin]+1); } } return dp[amount]<=amount ? dp[amount] : -1; }}

如果这篇文章对你有帮助,请点赞让我知道吧,我会更加努力给大家分享好的内容,谢谢~

转载地址:http://mfrsi.baihongyu.com/

你可能感兴趣的文章
shared_ptr的一些尴尬
查看>>
C++总结8——shared_ptr和weak_ptr智能指针
查看>>
c++写时拷贝1
查看>>
Linux网络编程---I/O复用模型之poll
查看>>
Java NIO详解
查看>>
在JS中 onclick="save();return false;"return false是
查看>>
idea 有时提示找不到类或者符号
查看>>
matplotlib.pyplot.plot()参数详解
查看>>
MFC矩阵运算
查看>>
ubuntu 安装mysql
查看>>
c# 计算器
查看>>
C# 简单的矩阵运算
查看>>
gcc 常用选项详解
查看>>
c++输出文件流ofstream用法详解
查看>>
firewalld的基本使用
查看>>
Linux下SVN客户端使用教程
查看>>
Linux分区方案
查看>>
nc 命令详解
查看>>
如何使用 systemd 中的定时器
查看>>
git命令速查表
查看>>