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

最长递增子序列(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.xdnf.cn/news/19215.html

相关文章:

  • 【69页PPT】智慧工厂数字化工厂蓝图规划建设方案(附下载方式)
  • 【计算机组成原理】LRU计数器问题
  • 项目管理的五个阶段是什么
  • 关于PXIe工控机的网速问题XH-PXIe7313万兆网卡
  • Java学习day_14之API(正则表达式)
  • 生成式BI工具(WrenAI)
  • rhel-server-7.9-x86_64-dvd.iso
  • AFSIM仿真工具介绍与源码编译
  • 【开题答辩全过程】以 靖西市旅游网站为例,包含答辩的问题和答案
  • [Oracle] LENGTH()函数
  • php电子签名
  • 【C++】掌握string类操作:高效处理字符串
  • 3D生成模型-NeRF:用神经辐射场定义视图合成
  • Ferris Wheel (贪心 | 双指针)
  • ubuntu 安装conda, ubuntu24安装miniConda
  • 【Pytorch】生成对抗网络实战
  • 服务器托管多少钱一年?服务器托管收费标准
  • React useState基本使用
  • 3000. 对角线最长的矩形的面积
  • linux系统学习(4.常用命令)
  • 【具身智能】【机器人动力学】台大林佩群笔记-待持续更新
  • 算法(④KMP)
  • 基于YOLO8的垃圾识别检测系统(数据集+源码+文章)
  • (双指针)Leetcode283.移动零-替换数字类别+Leetcode15. 三数之和
  • day44-Ansible变量
  • ESP32C3和ESP32S3的区别有哪些?该怎么选型?
  • React Router 6 获取路由参数
  • 无人机也能称重?电力巡检称重传感器安装与使用指南
  • 算法之x数之和
  • B树与B+树的原理区别应用