LeetCode300.最长递增子序列

题目

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

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

示例

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

思路

要找到一个数组中最长严格递增子序列的长度,可以使用动态规划 来解决这个问题。

我们再来介绍以下动态规划:

动态规划(Dynamic Programming,简称DP)是一种常见的解决问题的算法思想,通常用于求解具有重叠子问题和最优子结构性质的问题。动态规划算法通过将原问题分解为相对简单的子问题,并保存子问题的解,从而避免重复计算,提高了算法的效率。

动态规划算法通常适用于具有以下两个特点的问题:

  1. 最优子结构:问题的最优解可以通过子问题的最优解来求解。
  2. 重叠子问题:问题的计算过程中包含重复的子问题。

在动态规划算法中,一般采用自底向上或自顶向下的方式来解决问题。下面是动态规划算法的一般步骤:

  1. 确定状态:找到子问题的描述,定义状态表示问题的解。
  2. 确定状态转移方程:找到子问题之间的递推关系,即如何通过子问题的解来求解原问题的解。
  3. 边界条件:确定边界条件,即最小的子问题的解。
  4. 计算顺序:确定问题的计算顺序,一般可以采用自底向上或自顶向下的方式进行计算。
  5. 实现算法:根据以上步骤实现动态规划算法。

动态规划算法的核心思想是将原始问题分解成相对简单的子问题,并通过保存子问题的解避免重复计算,从而提高算法的效率。它在很多实际问题中都有广泛的应用,如最短路径问题、背包问题、字符串匹配等。

本题的动态规划解法如下:

  1. 创建一个与原始数组大小相同的dp数组,dpi表示以numsi结尾的最长递增子序列的长度。
  2. 初始化dp数组的每个元素为1,因为每个单独的元素本身就构成一个长度为1的递增子序列。
  3. 遍历数组,对于每个位置i,再遍历其之前的所有位置j(0 <= j < i),如果numsi大于numsj,说明可以将numsi接在以numsj结尾的递增子序列后面形成更长的递增子序列,即更新dpi = max(dpi, dpj + 1)。
  4. 最终找到dp数组中的最大值,即为最长严格递增子序列的长度。

Code

cpp 复制代码
int lengthOfLIS(vector<int>& nums) {
    int n = nums.size();
    vector<int> dp(n, 1);
    int maxLength = 1;
    
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j]) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
        maxLength = max(maxLength, dp[i]);
    }
    
    return maxLength;
}

这样,通过动态规划算法,可以高效地找到给定数组中最长严格递增子序列的长度。

但是考虑到通过DP解的话时间复杂度为O(n*n) ,时间复杂度比较高。

因此我们可以通过贪心+二分查找的解法降低时间复杂度。

力扣官方题解如下:


Code

cpp 复制代码
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int len = 1, n = (int)nums.size();
        if (n == 0) {
            return 0;
        }
        vector<int> d(n + 1, 0);
        d[len] = nums[0];
        for (int i = 1; i < n; ++i) {
            if (nums[i] > d[len]) {
                d[++len] = nums[i];
            } else {
                int l = 1, r = len, pos = 0; // 如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 pos 设为 0
                while (l <= r) {
                    int mid = (l + r) >> 1;
                    if (d[mid] < nums[i]) {
                        pos = mid;
                        l = mid + 1;
                    } else {
                        r = mid - 1;
                    }
                }
                d[pos + 1] = nums[i];
            }
        }
        return len;
    }
};
相关推荐
find1star5 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表
迅翼SwiftWing6 小时前
编队系列(总)|固定翼编队如何实现稳定飞行?通信架构、协同算法与工程约束深度解析
算法·架构
LB21126 小时前
力扣160 21 86
算法·leetcode·职场和发展
Tairitsu_H6 小时前
[C++] 深入理解红黑树:封装set与map
开发语言·c++·set·map·红黑树·模拟实现
小智老师PMP7 小时前
2026深度解析|PMP第八版与NPDP核心侧重点本质区别(管理类证书怎么选)
开发语言·分布式·算法·职场和发展·产品经理
HugoStudio_SWAN7 小时前
洛谷 B4450 / B3867 / B3923 智慧购物、储蓄与做题——从程序到生活
c++·学习·程序人生·算法·生活
a187927218317 小时前
【算法】动态规划第二篇:双序列 DP 三课——继承、计步与断链
算法·leetcode·动态规划·dp·回溯·暴力·算法讲解
江畔柳前堤8 小时前
前台·中台·后台:2026年AI原生时代的架构全景图
开发语言·人工智能·算法·机器学习·架构·scala·ai-native
文心快码BaiduComate9 小时前
2026 OPC x AI Coding 趋势案例交流会圆满落幕:一个人就一支研发团队
算法
ACM-moran9 小时前
条件显化(二分单峰函数+构造数列)
算法·推公式·整数三分