最长递增子序列

这是一个关于如何使用动态规划(Dynamic Programming, DP)求解"最长递增子序列"问题的详细博客。内容包括了题目分析、思路解析、案例解释以及代码实现,帮助你深入理解该问题的解法。


📌 题目分析

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

  • 输入 :一个整数数组 nums
  • 输出:最长递增子序列的长度(严格递增,子序列中的元素需保持相对顺序)。

示例

  • 输入: [10, 9, 2, 5, 3, 7, 101, 18]
  • 输出: 4
  • 解释: 最长递增子序列为 [2, 3, 7, 101],长度为 4

🧠 思路分析

这个问题适合用 动态规划 来解决。动态规划的核心思想是将大问题分解成小问题,并将每个小问题的解存储下来,以避免重复计算。我们可以用一个数组 dp 来记录每个位置的最长递增子序列的长度。算法过程如下:

  1. 初始化 dp 数组,每个元素的初始值为 1,因为每个元素本身就是一个长度为 1 的递增子序列。
  2. 从数组的第一个元素开始,遍历每个元素 i,对于每个 i,再遍历 i 之前的所有元素 j
  3. nums[j] < nums[i],则 nums[i] 可以接在 nums[j] 之后构成递增序列。此时更新 dp[i] 的值:dp[i] = max(dp[i], dp[j] + 1)
  4. 遍历完所有元素后,dp 数组中最大的值即为最长递增子序列的长度。

动态规划状态转移方程

dp[i] 表示以 nums[i] 结尾的最长递增子序列长度,则:

d p i = max ⁡ ( d p i , d p j + 1 ) for j < i and n u m s j < n u m s i dpi = \max(dpi, dpj + 1) \quad \text{for } j < i \text{ and } numsj < numsi dpi=max(dpi,dpj+1)for j<i and numsj<numsi

最终结果为 dp 数组的最大值 \max(dp)

时间复杂度

  • 时间复杂度:O(n²),其中 n 是数组的长度。原因是我们对每个元素进行一轮遍历。
  • 空间复杂度 :O(n),因为我们用了一个长度为 n 的 dp 数组。

📊 案例解释

案例 1

输入: [10, 9, 2, 5, 3, 7, 101, 18]

过程
  • 初始化:dp = [1, 1, 1, 1, 1, 1, 1, 1],每个元素的初始长度为 1
  • nums[0]nums[7] 逐步更新 dp 值:
  1. i = 3dp[3] = max(dp[3], dp[2] + 1) = 2(即 [2, 5])。
  2. i = 4dp[4] = 2(即 [2, 3])。
  3. i = 5dp[5] = 3(即 [2, 3, 7])。
  4. i = 6dp[6] = 4(即 [2, 3, 7, 101])。
  5. i = 7dp[7] = 4(即 [2, 3, 7, 18])。

最终 dp 数组为 [1, 1, 1, 2, 2, 3, 4, 4],最大值为 4

案例 2

输入: [0, 1, 0, 3, 2, 3]

  • 过程
    • dp[1] = 2(即 [0, 1]
    • dp[3] = 3(即 [0, 1, 3]
    • dp[4] = 3(即 [0, 1, 2]
    • dp[5] = 4(即 [0, 1, 2, 3]
  • 最长递增子序列的长度为 4

🔧 代码实现

python代码

python 复制代码
import os
import sys

# 请在此输入您的代码

def print_arr(arr):
  for i in range(len(arr)):
    if i == 0:
      print(arr[i],end='')
    else:
      print(' ',arr[i],end='')

def init_arr(size):
  return [1] * size

def solve(dp,arr):
  for i in range(0,len(arr)):
    for j in range(0,i):
      if arr[i] > arr[j]:
        dp[i] = max(dp[i],dp[j]+1)
  max_len = max(dp)
  max_len_index = dp.index(max_len)
  res = [arr[max_len_index]]
  max_len -= 1
  idx = max_len_index - 1
  while max_len > 0:
    if dp[idx] != dp[idx + 1]:
      res.append(arr[idx])
      max_len -= 1
    idx -= 1
  return list(reversed(res))

n = int(input())

arr = list(map(int,input().split()))
dp = init_arr(len(arr))
res = solve(dp,arr)
print_arr(res)

C++代码

c 复制代码
#include<bits/stdc++.h>
using namespace std;

const int max_len = 1024;

int dp[max_len];

void print_arr(int *, int);
void solve(int *, int);

int main(){
	int n;
	cin>>n;
	int a[n];
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	fill(dp,dp+max_len,1);
	solve(a,n);
	return 0;
}

void solve(int* a, int arr_len){
	for(int i=0;i<arr_len;i++){
		for(int j=0;j<i;j++){
			if (*(a+i) > *(a+j)){
				dp[i] = max(dp[i],dp[j]+1);
			}
		}
	}
	print_arr(dp,arr_len);
	int* max_value_ptr = max_element(dp,dp+arr_len);
	int max_value = *max_value_ptr;
	int r_size = max_value;
	int max_value_idx = max_value_ptr - dp;
	int* res = (int*) malloc(sizeof(int)*r_size);
	int j = r_size - 1;
	res[j] = a[max_value_idx];
	max_value --;
	int idx = max_value_idx - 1;
	while(max_value > 0){
		if(dp[idx] != dp[idx+1]){
			res[--j]=a[idx];
			max_value --;
		}
		idx--;
	}
	print_arr(res,r_size);
}

void print_arr(int* a, int arr_len){
	for(int i=0;i<arr_len;i++){
		if(i==0){
			cout<<a[i];
		}else{
			cout<<" "<<a[i];
		}
	}
}

代码说明

  1. 初始化 dpdp[i] 表示以 nums[i] 结尾的最长递增子序列的长度,初始值为 1
  2. 双重循环 :遍历 nums 的每一对 (j, i) 元素,更新 dp[i]
  3. 返回结果max(dp) 返回最长递增子序列的长度。

📈 总结

  • 动态规划 在求解最长递增子序列问题时,能够高效记录每个位置的状态,避免重复计算。
  • dp 数组 记录了以每个元素为终点的最长递增子序列的长度,从而将问题分解为小问题进行求解。
  • 这种方法尽管时间复杂度是 O(n²),但可以应对大部分常见的序列长度,如几千个元素的序列。
相关推荐
天佑木枫3 分钟前
15天Python入门系列 · 序
开发语言·python
happylifetree3 分钟前
Python017-第二章15.数据容器-dict常用操作
python
装不满的克莱因瓶18 分钟前
了解 LangChain 中的 LLM 与 ChatModel 的差异
人工智能·python·ai·langchain·llm·agent·chatmodel
手写码匠25 分钟前
从零实现 Prompt 工程引擎:结构化提示、自动优化与多轮自省体系
人工智能·深度学习·算法·aigc
无限码力1 小时前
阿里算法岗 0530笔试真题 - 多约束条件下的元素匹配统计
算法·阿里笔试真题·阿里机试真题·阿里算法岗笔试
lqqjuly1 小时前
MLA — 多头潜在注意力深度解析
深度学习·神经网络·算法
IT知识分享1 小时前
从零开发在线简繁转换工具:OpenCC 实战、避坑经验与方案选型
javascript·python
lunzi_08261 小时前
【学习笔记】《Python编程 从入门到实践》第8章:函数定义、参数传递与模块导入
笔记·python·学习
吴可可1231 小时前
SolidWorks草图转三维DWG技巧
算法
凡人叶枫1 小时前
Effective C++ 条款04:确定对象被使用前已先被初始化
java·linux·开发语言·c++·嵌入式开发