代码随想录算法训练营第五十二天 | LeetCode 300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

简介: 代码随想录算法训练营第五十二天 | LeetCode 300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

代码随想录算法训练营第五十二天 | LeetCode 300. 最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

文章链接:最长递增子序列最长连续递增序列最长重复子数组

视频链接:最长递增子序列最长连续递增序列最长重复子数组

1. LeetCode 300. 最长递增子序列

1.1 思路

  1. 本题是属于子序列系列,同样是动态规划解决的经典的一系列问题。
  2. dp 数组及其下标的含义:dp[i] 表示以 nums[i] 为结尾的最长递增子序列的长度为 dp[i]
  3. 递推公式:设 j 在 [0,i)之间,当 nums[i]>nums[j] 时,nums[i] 可以接在 nums[j] 之后(严格递增时),就相当于 nums[i] 是直接加在 dp[j] 原来的递增子序列后的,此时 dp[i]=dp[j]+1;当 nums[i]<=nums[j] 时,nums[i] 不可以接在 nums[j] 之后,就 dp[i]=dp[i]。因此 dp[i]=Math.max(dp[i],dp[j]+1)
  4. dp 数组的初始化:dp[i] 至少都是 1,意思是至少包含 nums[i]
  5. 遍历顺序:这里都依赖前面的元素,因此 i 一定是从左到右遍历,for(int i=1;i<nums.length;i++)为什么从 1 开始?因为 dp[0]=1 没必要求了,肯定为 1。for(int j=0;j<i;j++)遍历所有 nums[j] 的情况和 nums[i] 做比较,j 从前往后还是从后往前呢?其实都可以,只要把 [0,i) 之间都遍历了就可以了。if(nums[i]>nums[j])dp[i]=Math.max(dp[i],dp[j]+1)
  6. 最终结果存在哪呢?是在 dp 数组的最大值,就遍历 dp 数组,result=Math.max(dp[i],result),最终返回 result 即可
  7. 打印 dp 数组:用于 debug

1.2 代码

class Solution {
    public int lengthOfLIS(int[] nums) {
        int[] dp = new int[nums.length];
        int res = 0;
        Arrays.fill(dp, 1);
        for (int i = 1; i < dp.length; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[i] > nums[j]) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
                res = Math.max(res, dp[i]);
            }
        }
        return res;
    }
}

2. LeetCode 674. 最长连续递增序列

2.1 思路

  1. 本题和300. 最长递增子序列的区别就在于要求连续。
  2. dp 数组及其下标的含义:dp 表示以 i 为结尾的最长连续递增子序列为 dp[i]
  3. 递推公式:因为本题要求连续,就不需要比较前面所有的元素了,直接比较 nums[i] 和 nums[i-1],如果 nums[i]>nums[i-1] 就 dp[i]=dp[i-1]+1,就相当于 nums[i] 直接接在了 dp[i-1] 后面了,因此就 dp[i-1]+1
  4. dp 数组的初始化:dp 数组全部都初始化为 1,因为每个元素本身就是连续的,就是 1
  5. 遍历顺序:因为 i 依赖 i-1,因此是从前往后遍历,for(int i=1;i<nums.length;i++)这里为什么从 1 开始?因为 dp[0] 一定是 1。
  6. 最终结果在哪里?是在 dp 数组的最大值中,因此遍历 dp 数组,result=Math.max(dp[i],result),最终返回 result
  7. 打印 dp 数组:用于 debug

2.2 代码

/**
     * 1.dp[i] 代表当前下标最大连续值
     * 2.递推公式 if(nums[i+1]>nums[i]) dp[i+1] = dp[i]+1
     * 3.初始化 都为1
     * 4.遍历方向,从其那往后
     * 5.结果推导 。。。。
     * @param nums
     * @return
     */
    public static int findLengthOfLCIS(int[] nums) {
        int[] dp = new int[nums.length];
        for (int i = 0; i < dp.length; i++) {
            dp[i] = 1;
        }
        int res = 1;
  //可以注意到,這邊的 i 是從 0 開始,所以會出現和卡哥的C++ code有差異的地方,在一些地方會看到有 i + 1 的偏移。
        for (int i = 0; i < nums.length - 1; i++) {
            if (nums[i + 1] > nums[i]) {
                dp[i + 1] = dp[i] + 1;
            }
            res = res > dp[i + 1] ? res : dp[i + 1];
        }
        return res;
    }

3. LeetCode 718. 最长重复子数组

3.1 思路

  1. 求两个数组的最长重复子数组的长度,其实就是求最长连续子序列。
  2. dp 数组及其下标的含义:我们用一个二维 dp 数组保存我们两个数组比较的状态。dp[i][j] 表示的是最长重复子数组,第一个数组到 i,第二个数组到 j,最长重复子数组为 dp[i][j]。即 dp[i][j] 表示以 i-1 为结尾的数组 1 和以 j-1 为结尾的数组 2,最长重复子数组长度为 dp[i][j]。为什么不以 i 和 j 为结尾呢?那么定义是为了简洁
  3. 递推公式:if(nums1[i-1]==nums2[j-1])就相当于找到两个数组中相等的元素了 dp[i][j]=dp[i-1][j-1]+1,为什么是从 i-1,j-1 这里+1 呢?因为我们是比较两个数组中的元素是否相同,如果相同就要两个数组一起往后退一格,一起看后面元素的状态,从这个
  4. dp 数组的初始化:dp[i][0] 和 dp[0][j] 就是第一列和第一行都是没有意义的状态,因为我们要用到 i-1 和 j-1,因此应该初始化为 0,因为 dp 数组是在前一个状态+1,这里没有意义就不要初始化为 1 了。其余位置会被递推公式覆盖,就可以不用初始化,默认为 0 即可
  5. 遍历顺序:肯定是两层 for 循环,先遍历数组 1 还是数组 2 呢?其实都可以。这里 for(int i=1;i<=nums1.length;i++)for(int j=1;j<=nums2.length;j++)因为 0 这一行和列没有意义,直接都从 1 开始。为什么取等于号呢?我们的 dp 数组的长度是 nums1 和 nums2 的长度加 1 的,而我们比较的时候是比较 nums[i-1] 和 nums[j-1],因此不会越界。
  6. 最终结果存在哪?不是在最后面的位置的,因此要遍历二维数组,找到最大值,因此在两层 for 循环遍历的时候就记录最大值了,再第二层 for 循环中最后 if(dp[i][j]>result)result=dp[i][j]
  7. 打印 dp 数组:用于 debug
  8. 解释:为什么不定义 i 和 j 为结尾的子序列呢?如果这样的话,第一列和第一行就要遍历统计,如果对应位置相等了,就要初始化为 1 了,就比较麻烦了

3.2 代码

// 版本一
class Solution {
    public int findLength(int[] nums1, int[] nums2) {
        int result = 0;
        int[][] dp = new int[nums1.length + 1][nums2.length + 1];
        for (int i = 1; i < nums1.length + 1; i++) {
            for (int j = 1; j < nums2.length + 1; j++) {
                if (nums1[i - 1] == nums2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                    result = Math.max(result, dp[i][j]);
                }
            }
        }
        return result;
    }
}
相关文章
|
3月前
|
算法
【算法】滑动窗口——长度最小的子数组
【算法】滑动窗口——长度最小的子数组
|
1月前
|
算法
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
这篇文章介绍了动态规划算法中解决最大上升子序列问题(LIS)的方法,包括问题的描述、动态规划的步骤、状态表示、递推方程、计算最优值以及优化方法,如非动态规划的二分法。
61 0
动态规划算法学习四:最大上升子序列问题(LIS:Longest Increasing Subsequence)
|
2月前
|
算法 数据可视化
基于SSA奇异谱分析算法的时间序列趋势线提取matlab仿真
奇异谱分析(SSA)是一种基于奇异值分解(SVD)和轨迹矩阵的非线性、非参数时间序列分析方法,适用于提取趋势、周期性和噪声成分。本项目使用MATLAB 2022a版本实现从强干扰序列中提取趋势线,并通过可视化展示了原时间序列与提取的趋势分量。代码实现了滑动窗口下的奇异值分解和分组重构,适用于非线性和非平稳时间序列分析。此方法在气候变化、金融市场和生物医学信号处理等领域有广泛应用。
114 19
|
2月前
|
算法 数据可视化 数据安全/隐私保护
基于LK光流提取算法的图像序列晃动程度计算matlab仿真
该算法基于Lucas-Kanade光流方法,用于计算图像序列的晃动程度。通过计算相邻帧间的光流场并定义晃动程度指标(如RMS),可量化图像晃动。此版本适用于Matlab 2022a,提供详细中文注释与操作视频。完整代码无水印。
|
3月前
|
机器学习/深度学习 数据采集 算法
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
本文介绍了一个基于Python的时间序列模型,用于分析和预测2021-2022年重庆地区的气温变化趋势,通过ARIMA和LSTM模型的应用,揭示了气温的季节性和趋势性变化,并提供了对未来气温变化的预测,有助于气象预报和相关决策制定。
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
|
3月前
|
算法 Python
【Leetcode刷题Python】子数组查找
一个用于寻找给定字符串中最长重复子串的Python函数实现,采用了滑动窗口的方法来检测重复的子串。
20 1
|
3月前
|
算法 C++
【算法】前缀和算法——和可被K整除的子数组
【算法】前缀和算法——和可被K整除的子数组
|
3月前
|
算法
【算法】前缀和算法——和为k的子数组之和
【算法】前缀和算法——和为k的子数组之和
|
3月前
|
算法
【算法】栈算法——栈的压入、弹出序列
【算法】栈算法——栈的压入、弹出序列
|
3月前
|
算法 Java
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
LeetCode初级算法题:子数组最大平均数+二叉树的最小深度+最长连续递增序列+柠檬水找零
40 0