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

最长递增子序列(LIS)的 DFS 解法详解与实现

题目描述

给定一个整数数组(如 [10,9,2,5,3,7,101,18]),计算其最长递增子序列的长度。其中,子序列不要求连续,仅需满足元素严格递增的顺序即可。

例如:

  • 输入数组 [10,9,2,5,3,7,101,18]
  • 最长递增子序列可为 [2,3,7,18] 或 [2,5,7,101]
  • 输出结果:4

解题思路:深度优先搜索(DFS)

核心思想

最长递增子序列的本质是 “在每个元素位置做选择”——选当前元素(若满足递增条件,子序列长度 + 1)或不选当前元素(子序列长度不变)。通过深度优先搜索遍历所有可能的选择,记录其中的最大长度,即可得到答案。

关键参数设计

我们设计 dfs 函数包含三个核心参数,清晰描述当前搜索状态:

  • i:当前遍历到的数组索引(表示 “正在处理第 i 个元素”)
  • last:上一个被选入子序列的元素值(用于判断当前元素是否满足 “递增” 条件)
  • len:当前子序列的长度(实时记录当前路径的子序列长度)

递归逻辑

  1. 更新最大长度:每次进入递归函数,先判断当前子序列长度是否超过历史最大值,若超过则更新结果。
  2. 终止条件:当 i >= nums.size() 时(遍历完所有元素),直接返回,结束当前递归分支。
  3. 选择 1:选当前元素:若 nums[i] > last(满足递增),则递归处理下一个元素,此时 last 更新为 nums[i]len 加 1。
  4. 选择 2:不选当前元素:无论是否满足递增,都可跳过当前元素,递归处理下一个元素,last 和 len 保持不变。

完整代码实现

#include <iostream>
#include <vector>
using namespace std;// 全局变量:存储输入数组和最终结果(最长递增子序列长度)
vector<int> nums;
int result = -1;  // 初始化为-1,避免数组为空时逻辑错误/*** 深度优先搜索函数:遍历所有可能的子序列,计算最长递增子序列长度* @param i 当前遍历到的数组索引* @param last 上一个被选入子序列的元素值* @param len 当前子序列的长度*/
void dfs(int i, int last, int len) {// 每次递归先更新最大长度(确保所有可能的子序列长度都被考虑)if (result < len) {result = len;}// 终止条件:遍历完所有元素,结束当前分支if (i >= nums.size()) {return;}// 选择1:选当前元素(需满足递增条件)if (nums[i] > last) {dfs(i + 1, nums[i], len + 1);}// 选择2:不选当前元素(无论是否满足递增,都可跳过)dfs(i + 1, last, len);
}int main() {// 读取输入数组(支持空格分隔,回车结束输入)int a;cout << "请输入整数数组(空格分隔,回车结束):";while (cin >> a) {nums.push_back(a);// 检测到回车时停止输入if (cin.peek() == '\n') {break;}}// 初始调用DFS:从索引0开始,last初始为-1(表示暂无选中元素),初始长度为0dfs(0, -1, 0);// 输出结果cout << "最长递增子序列的长度为:" << result << endl;return 0;
}

代码测试与验证

测试用例 1:常规数组

  • 输入:10 9 2 5 3 7 101 18
  • 输出:4
  • 说明:最长递增子序列为 [2,3,7,18] 或 [2,5,7,101],长度均为 4。

测试用例 2:空数组

  • 输入:(直接回车)
  • 输出:0
  • 说明:空数组无元素,最长子序列长度为 0(代码中 result 初始为 - 1,但递归初始调用 len=0 会更新 result 为 0)。

测试用例 3:递减数组

  • 输入:5 4 3 2 1
  • 输出:1
  • 说明:递减数组的最长递增子序列为单个元素,长度为 1。

测试用例 4:递增数组

  • 输入:1 2 3 4 5
  • 输出:5
  • 说明:完整数组即为最长递增子序列,长度为 5。

算法分析

时间复杂度

  • 复杂度:O(2^n),其中 n 为数组长度。
  • 原因:每个元素有 “选” 和 “不选” 两种选择,递归会生成 2^n 个分支(实际因递增条件会减少部分分支,但最坏情况仍为 O(2^n))。

空间复杂度

  • 复杂度:O(n)
  • 原因:递归深度等于数组长度(最坏情况需遍历所有元素才终止),栈空间占用为 O(n);无额外辅助空间(全局变量不计入算法额外空间)。

优缺点与优化方向

优点

  • 逻辑直观:直接模拟 “选或不选” 的决策过程,易于理解和实现。
  • 无需预处理:直接对原始数组递归,代码简洁。

缺点

  • 效率较低:O(2^n) 的时间复杂度在 n > 20 时会明显超时,无法处理大规模数组。

优化方向

若需处理更大规模的数组(如 n > 1000),可改用以下更高效的算法:

  1. 动态规划(DP):时间复杂度 O(n^2),空间复杂度 O(n)(定义 dp[i] 为以 nums[i] 结尾的最长递增子序列长度)。
  2. 贪心 + 二分查找:时间复杂度 O(n log n),空间复杂度 O(n)(维护一个 “最小递增序列”,用二分查找优化更新过程)。

总结

本文通过深度优先搜索(DFS)实现了最长递增子序列的求解,核心是遍历 “选或不选” 的所有可能分支,记录最大子序列长度。该方法逻辑清晰,适合理解 LIS 问题的本质,但受限于 O(2^n) 的时间复杂度,仅适用于小规模数组。若需处理大规模数据,建议进一步学习动态规划或贪心 + 二分查找的优化方案

http://www.dtcms.com/a/359196.html

相关文章:

  • Session
  • PB-重装系统后,重新注册ole控件,pb中窗口控件失效的问题。
  • 2025年06月GESPC++三级真题解析(含视频)
  • 【小宁学习日记5 PCB】电路定理
  • Unity游戏打包——GooglePlay自动传包
  • DFS 回溯 【各种题型+对应LeetCode习题练习】
  • 【多项式】快速莫比乌斯变换(FMT)
  • CCS自定义函数.h与.c问题解决办法
  • Android15适配16kb
  • 计算机毕设项目 基于Python与机器学习的B站视频热度分析与预测系统 基于随机森林算法的B站视频内容热度预测系统
  • Robolectric拿到当前的Activity
  • 轻量化模型-知识蒸馏1
  • Wheat Gene ID Convert Tool 小麦中国春不同参考基因组GeneID转换在线工具
  • 2025年外贸服装跟单管理软件TOP3推荐榜单
  • 手动安装的node到nvm吧版本管理的过程。
  • 基于Docker部署的Teable应用
  • [特殊字符]️ STL 容器快速参考手册
  • 海盗王64位dx9客户端修改篇之三
  • 【有序集合 有序映射 懒删除堆】 3510. 移除最小数对使数组有序 II|2608
  • 9. 函数和匿名函数(一)
  • enumerate 和for in搭配使用
  • 接雨水,leetCode热题100,C++实现
  • 【随笔】【Debian】【ArchLinux】基于Debian和ArchLinux的ISO镜像和虚拟机VM的系统镜像获取安装
  • C++的迭代器和指针的区别
  • 「日拱一码」066 深度学习——Transformer
  • Flutter MVVM+provider的基本示例
  • Qt中的锁和条件变量和信号量
  • Science:机器学习模型进行遗传变异外显率预测
  • 线段树相关算法题(5)
  • 【大语言模型 30】指令微调数据工程:高质量数据集构建