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

LeetCode 300 最长递增子序列

https://leetcode.cn/problems/longest-increasing-subsequence/description/?envType=study-plan-v2&envId=selected-coding-interview

方法一:动态规划(基础解法)

#include <vector>
#include <algorithm>
using namespace std;class Solution {
public:int lengthOfLIS(vector<int>& nums) {int n = nums.size();if (n == 0) return 0;vector<int> dp(n, 1);  // dp[i] 表示以nums[i]结尾的LIS长度int maxLen = 1;for (int i = 0; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[j] < nums[i]) {dp[i] = max(dp[i], dp[j] + 1);}}maxLen = max(maxLen, dp[i]);  // 更新全局最大值}return maxLen;}
};

方法二:贪心 + 二分查找解法

#include <vector>
#include <algorithm>
using namespace std;class Solution {
public:int lengthOfLIS(vector<int>& nums) {vector<int> tails;  // tails[k] 表示长度为k+1的LIS的末尾最小元素for (int num : nums) {// 二分查找第一个 >= num 的位置auto it = lower_bound(tails.begin(), tails.end(), num);if (it == tails.end()) {tails.push_back(num);  // 可以扩展LIS长度} else {*it = num;  // 替换为更小的末尾元素}}return tails.size();}
};

这段代码是贪心 + 二分查找解法的核心逻辑,用于维护一个数组 tails,其中 tails[k] 表示长度为 k+1递增子序列的末尾最小元素。以下是对 if (it == tails.end()) 条件的详细解释:

1. lower_bound 的作用

auto it = lower_bound(tails.begin(), tails.end(), num);

  • 功能:在有序数组 tails 中查找第一个大于等于 num 的元素位置
  • 返回值
    • 若存在这样的元素,返回其迭代器;
    • 若不存在(即 num 大于所有元素),返回 tails.end()

2. 条件判断逻辑

情况一:it == tails.end()
  • 含义num 大于 tails 中的所有元素。
  • 操作tails.push_back(num)
  • 意义
    • 可以形成一个更长的递增子序列。例如:
      • tails = [2, 3, 7],当前 num = 101,则 101 可接在 7 后面,形成长度为 4 的子序列 [2, 3, 7, 101]
    • tails 的长度直接增加 1,对应最长递增子序列的长度加 1
情况二:it != tails.end()
  • 含义tails 中存在第一个大于等于 num 的元素,记为 tails[i]
  • 操作*it = num(等价于 tails[i] = num)。
  • 意义
    • 用更小的 num 替换 tails[i],使得后续元素更容易扩展出更长的子序列。例如:
      • tails = [2, 3, 7, 101],当前 num = 18,则 18 < 101,用 18 替换 101 后,tails 变为 [2, 3, 7, 18]
      • 这样,未来若有元素 20,可以接在 18 后面形成长度为 4 的子序列,而原来的 101 可能无法容纳更小的后续元素。
    • 贪心策略:在保持子序列长度不变的前提下,尽可能让末尾元素更小,为后续扩展留有余地。

3. 举例说明

以输入 nums = [2, 5, 3, 7, 101, 18] 为例,看 tails 的变化过程:

  1. 处理 2
    • tails 为空,lower_bound 返回 endtails.push_back(2)tails = [2]
  2. 处理 5
    • lower_bound 找到 2 < 5,返回 endtails.push_back(5)tails = [2, 5]
  3. 处理 3
    • lower_bound[2,5] 中查找第一个 ≥3 的元素,找到 5(索引 1)。
    • 3 替换 5tails = [2, 3]。此时长度仍为 2,但末尾元素更小。
  4. 处理 7
    • lower_bound 返回 endtails.push_back(7)tails = [2, 3, 7]
  5. 处理 101
    • lower_bound 返回 endtails.push_back(101)tails = [2, 3, 7, 101]
  6. 处理 18
    • lower_bound[2,3,7,101] 中查找第一个 ≥18 的元素,找到 101(索引 3)。
    • 18 替换 101tails = [2, 3, 7, 18]。此时长度仍为 4,但末尾元素更小,有利于后续扩展。

4. 为什么这样做能得到正确结果?

  • tails 的性质
    tails 始终是递增数组(因为每次添加元素都在末尾,替换操作也不会破坏递增顺序)。
  • 长度的正确性
    tails 的长度始终等于当前最长递增子序列的长度。因为每次 push_back 都会增加长度,而替换操作不会改变长度。
  • 末尾元素的最优性
    对于每个可能的长度 ktails[k-1] 是所有长度为 k 的递增子序列中末尾最小的元素,这使得后续元素更容易形成更长的子序列。

总结

  • if (it == tails.end()):判断当前元素是否能扩展递增子序列的长度,能则添加到末尾。
  • else:用当前元素替换 tails 中第一个大于等于它的元素,保证末尾元素尽可能小,为后续扩展留有余地。
  • 核心思想:通过贪心策略维护一个最小末尾元素的数组,结合二分查找实现O(nlogn)的高效解法。
http://www.lqws.cn/news/93313.html

相关文章:

  • MySQL关系型数据库学习
  • AWS VPC 网络详解:理解云上专属内网的关键要素
  • Windows 下彻底删除 VsCode
  • win11中使用grep命令
  • 【WPF】从普通 ItemsControl 到支持筛选的 ItemsControl:深入掌握 CollectionViewSource 用法
  • 大模型 提示模板 设计
  • 【快见刊】2025年应用材料、机械与制造工程国际会议(ICAMMME 2025)
  • 学习资料搜集-ARMv8 cache 操作
  • 《CF912E Prime Gift》
  • 破局与进阶:ueBIM 在国产 BIM 赛道的差距认知与创新实践
  • 默认网关 -- 负责转发数据包到其他网络的设备(通常是路由器)
  • 深度学习驱动的车牌识别:技术演进与未来挑战
  • C++:内存管理
  • JS对象——BOM
  • MySQL强化关键_019_索引优化
  • winrm登录失败,指定的凭据被服务器拒绝
  • 基于PyQt5的相机手动标定工具:原理、实现与应用
  • Python 数据分析与可视化实战:从数据清洗到图表呈现
  • Java IO
  • 黑马Java面试笔记之 集合篇(算法复杂度+ArrayList+)
  • WPS 利用 宏 脚本拆分 Excel 多行文本到多行
  • 题山采玉: Day1
  • 【AI Study】第三天,Python基础 - NumPy(1)
  • 【Redis】list 类型
  • 面向对象系统中对象交互的架构设计哲学
  • (四)动手实现多层感知机:深度学习中的非线性建模实战
  • OpenCV CUDA模块图像处理------双边滤波的GPU版本函数bilateralFilter()
  • 基于SDN环境下的DDoS异常攻击的检测与缓解
  • 二叉树day1
  • 如何使用插件和子主题添加WordPress自定义CSS(附:常见错误)