当前位置: 首页 > news >正文

Day43--动态规划--674. 最长连续递增序列,300. 最长递增子序列,718. 最长重复子数组

Day43–动态规划–674. 最长连续递增序列,300. 最长递增子序列,718. 最长重复子数组

674. 最长连续递增序列

方法:动态规划

思路:

  1. dp[i]含义:到i这个位置(包含i)的连续递增子序列的长度
  2. 递推公式:if (nums[i] > nums[i - 1]) dp[i] = dp[i - 1] + 1;
  3. 初始化:每一个nums[i]的子序列都至少是1,就是自己。Arrays.fill(dp, 1); int maxLen = 1;
  4. 遍历方向:正序
// 动态规划
class Solution {public int findLengthOfLCIS(int[] nums) {int n = nums.length;// dp[i]含义:到i这个位置(包含i)的连续递增子序列的长度int[] dp = new int[n];// 初始化:每一个nums[i]的子序列都至少是1,就是自己Arrays.fill(dp, 1);int maxLen = 1;// 从1开始遍历for (int i = 1; i < n; i++) {if (nums[i] > nums[i - 1]) {dp[i] = dp[i - 1] + 1;// 刷新最大值maxLen = Math.max(maxLen, dp[i]);}}return maxLen;}
}

方法:贪心

思路:

连续递增,就不断len++;不连续了,就更新最大值。

如果最长串,刚好到末尾的话,出了循环还要更新一次最大值。

class Solution {public int findLengthOfLCIS(int[] nums) {int n = nums.length;// 题目说n>1。所以至少有一个nums[i]。每个nums[i]的长度都是1int len = 1;int maxLen = 1;// 从1开始遍历(如果n==1的话,不进这个循环,答案也是对的)for (int i = 1; i < n; i++) {if (nums[i] > nums[i - 1]) {// 连续递增len++;} else {// 不连续了。刷新最大值maxLen = Math.max(maxLen, len);// 每个nums[i]的长度都是1len = 1;}}// 如果最长串,刚好到末尾的话。maxLen还没取到,所以出了循环还要再取一次return Math.max(maxLen, len);}
}

思路:

另一种写法

class Solution {public int findLengthOfLCIS(int[] nums) {int n = nums.length;// 题目说n>1。所以至少有一个nums[i]。每个nums[i]的长度都是1int len = 1;int maxLen = 1;// 从1开始遍历(如果n==1的话,不进这个循环,答案也是对的)for (int i = 1; i < n; i++) {if (nums[i] > nums[i - 1]) {// 连续递增len++;} else {// 重新开始,每个nums[i]的长度都是1len = 1;}// 如果有更大值,刷新maxLenif (len > maxLen) {maxLen = len;}}return maxLen;}
}

300. 最长递增子序列

方法:动态规划

思路:

  1. dp含义:dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
  2. 递归公式:
    • 本意就是取出最大的dp[j]+1。通俗点来讲,就是接在哪个j后面,能有最大长度。
    • if (nums[i] > nums[j]) dp[i] = Math.max(dp[i], dp[j] + 1);
  3. 初始化:每一个nums[i]的子序列都至少是1,就是自己Arrays.fill(dp, 1);
  4. 遍历方向:正序
// 动态规划
class Solution {public int lengthOfLIS(int[] nums) {int n = nums.length;// dp含义:dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度int[] dp = new int[n];// 题目说n>=1,所以res至少也有1int res = 1;// 初始化:每一个nums[i]的子序列都至少是1,就是自己Arrays.fill(dp, 1);// 从1开始遍历for (int i = 1; i < n; i++) {// 从[0-i]中找,所以复杂度是O(n^2)for (int j = 0; j < i; j++) {if (nums[i] > nums[j]) {// 注意这里的max函数,本意不是为了比较dp[i]和dp[j]+1,而是为了取出本层最大的dp[i],因为j在遍历嘛// 本意就是取出最大的dp[j]+1。通俗点来讲,就是接在哪个j后面,能有最大长度dp[i] = Math.max(dp[i], dp[j] + 1);}}// 更新resres = Math.max(res, dp[i]);}return res;}
}

方法:贪心+二分

思路来自力扣官方题解

思路:

  • 考虑一个简单的贪心,如果我们要使上升子序列尽可能的长,则我们需要让序列上升得尽可能慢,因此我们希望每次在上升子序列最后加上的那个数尽可能的小。

  • 基于上面的贪心思路,我们维护一个数组 d[i] ,表示长度为 i 的最长上升子序列的末尾元素,用 len 记录目前最长上升子序列的长度,起始时 len 为 1,d[1]=nums[0]。

  • 设当前已求出的最长上升子序列的长度为 len(初始时为 1),从前往后遍历数组 nums,在遍历到 nums[i] 时:

    • 如果 nums[i]>d[len] ,则直接加入到 d 数组末尾,并更新 len=len+1;
    • 否则,在 d 数组中二分查找,找到第一个比 nums[i] 小的数 d[k] ,并更新 d[k+1]=nums[i]。
// 贪心 + 二分(时间复杂度nlogn)
class Solution {public int lengthOfLIS(int[] nums) {int n = nums.length;if (n == 0) {return 0;}int len = 1;int[] d = new int[n + 1];d[len] = nums[0];for (int i = 1; i < n; i++) {if (nums[i] > d[len]) {d[++len] = nums[i];} else {// 调用二分查找函数寻找合适的位置int pos = binarySearch(d, 1, len, nums[i]);d[pos + 1] = nums[i];}}return len;}// 二分查找函数,单独提取出来private int binarySearch(int[] d, int left, int right, int target) {int pos = 0; // 如果找不到说明所有的数都比 target 大,此时要更新 d[1],所以这里将 pos 设为 0while (left <= right) {int mid = left - ((left - right) >> 1);if (d[mid] < target) {pos = mid;left = mid + 1;} else {right = mid - 1;}}return pos;}
}

718. 最长重复子数组

注意,这题虽然说是“子数组”,但是实际上是按“子序列”理解。

子数组是可以重新排序的,子序列是不能改变原有的顺序的(但是可以跳过一些元素)

方法:暴力

思路:

每当nums1[i] == nums2[j]的时候,记录while(nums1[ii++] == nums2[jj++])的长度。

但是这个方法的时间复杂度很恐怖,去到O(n^3)

class Solution {public int findLength(int[] nums1, int[] nums2) {int maxLen = 0;int n1 = nums1.length;int n2 = nums2.length;for (int i = 0; i < n1; i++) {for (int j = 0; j < n2; j++) {if (nums1[i] == nums2[j]) {int ii = i;int jj = j;while (ii < n1 && jj < n2 && nums1[ii] == nums2[jj]) {ii++;jj++;}int len = ii - i;if (len > maxLen) {maxLen = len;}}}}return maxLen;}
}

方法:动态规划(二维DP数组)

思路:

  1. dp[i][j]含义:以下标i - 1为结尾的A,和以下标j - 1为结尾的B,的最长重复子数组长度
  2. 递推公式:
    • 前面的一位是相等的,那么dp[i][j]要等于dp[i - 1][j - 1] + 1,表明以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组的长度增加了1
    • if (nums1[i - 1] == nums2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
  3. 初始化:0
  4. 遍历顺序:正序
// 动态规划(二维DP数组)
class Solution {public int findLength(int[] nums1, int[] nums2) {int n1 = nums1.length;int n2 = nums2.length;// dp[i][j]含义:以下标i - 1为结尾的A,和以下标j - 1为结尾的B,的最长重复子数组长度int[][] dp = new int[n1 + 1][n2 + 1];int maxLen = 0;// 要从索引1开始遍历,到索引n才结束for (int i = 1; i <= n1; i++) {for (int j = 1; j <= n2; j++) {// 前面的一位是相等的,那么dp[i][j]等于前面的长度+1。(告诉后面到我这有多长重复)if (nums1[i - 1] == nums2[j - 1]) {dp[i][j] = dp[i - 1][j - 1] + 1;}// 刷新maxLenif (dp[i][j] > maxLen) {maxLen = dp[i][j];}}}return maxLen;}
}

方法:动态规划(一维DP数组)

思路:

《代码随想录》:

我们可以看出dp[i][j]都是由dp[i - 1][j - 1]推出。那么压缩为一维数组,也就是dp[j]都是由dp[j - 1]推出。

也就是相当于可以把上一层dp[i - 1][j]拷贝到下一层dp[i][j]来继续用。

此时遍历B数组的时候,就要从后向前遍历,这样避免重复覆盖

// 动态规划(一维DP数组)
class Solution {public int findLength(int[] nums1, int[] nums2) {int n1 = nums1.length;int n2 = nums2.length;// 因为把nums1的维度压缩了。所以要靠nums2作为dp// 所以要遍历nums1,nums2每层的状态要复用int[] dp = new int[n2 + 1];int maxLen = 0;for (int i = 1; i <= n1; i++) {// 因为要用到上一层的状态,所以要倒序遍历for (int j = n2; j > 0; j--) {if (nums1[i - 1] == nums2[j - 1]) {dp[j] = dp[j - 1] + 1;} else {dp[j] = 0; // 注意,不相等的话,要刷回0}// 刷新maxLenif (dp[j] > maxLen) {maxLen = dp[j];}}}return maxLen;}
}
http://www.dtcms.com/a/326137.html

相关文章:

  • 算力板卡:AI时代的“算力心脏”
  • 指针和引用的区别
  • SQL中BETWEEN与IN的差异详解
  • Mybatis学习之缓存(九)
  • BM25算法记忆
  • 推荐中的在线学习
  • macos彻底删除vscode
  • Spring JDBC
  • 零基础AI编程开发微信小程序赚流量主广告实战
  • 6s081实验1
  • redis(2)-java客户端使用(IDEA基于springboot)
  • USB 基本描述符
  • Go 多进程编程-管道
  • C++方向知识汇总(三)
  • 面试实战 问题二十三 如何判断索引是否生效,什么样的sql会导致索引失效
  • git:分支
  • 3Ds Max的魔改利器:RailClone - 程序化建模的革命者
  • MySQL 经典练习 50 题(完美解答版,提供解题思路)
  • Spring Framework源码解析——DisposableBean
  • Oracle数据库中的Library cache lock和pin介绍
  • Java多线程并发控制:使用ReentrantLock实现生产者-消费者模型
  • js异步操作 Promise :fetch API 带来的网络请求变革—仙盟创梦IDE
  • 机器翻译:Bahdanau注意力和Luong注意力详解
  • 【浮点数存储】double类型注意点
  • 理解LangChain — Part 3:链式工作流与输出解析器
  • Notepad--:国产跨平台文本编辑器,Notepad++ 的理想替代方案
  • 写一篇Ping32和IP-Guard的对比,重点突出Ping32
  • 循环控制:break和continue用法
  • 鸿蒙flutter项目接入极光推送
  • Java项目基本流程(三)