最长递增子序列

这是一个关于如何使用动态规划(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 ] dp[i] = \max(dp[i], dp[j] + 1) \quad \text{for } j < i \text{ and } nums[j] < nums[i] dp[i]=max(dp[i],dp[j]+1)for j<i and nums[j]<nums[i]

最终结果为 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²),但可以应对大部分常见的序列长度,如几千个元素的序列。
相关推荐
懒大王爱吃狼1 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
劲夫学编程1 小时前
leetcode:杨辉三角
算法·leetcode·职场和发展
毕竟秋山澪1 小时前
孤岛的总面积(Dfs C#
算法·深度优先
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
浮生如梦_3 小时前
Halcon基于laws纹理特征的SVM分类
图像处理·人工智能·算法·支持向量机·计算机视觉·分类·视觉检测
深度学习lover3 小时前
<项目代码>YOLOv8 苹果腐烂识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·苹果腐烂识别
API快乐传递者4 小时前
淘宝反爬虫机制的主要手段有哪些?
爬虫·python
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法