1. 最长上升子序列问题的经典解法
最长上升子序列(Longest Increasing Subsequence, LIS)问题是动态规划领域的经典案例。给定一个无序的整数序列,我们需要找到其中最长的严格递增子序列的长度。例如对于序列[10, 9, 2, 5, 3, 7, 101, 18],最长上升子序列是[2, 3, 7, 101],长度为4。
1.1 基本动态规划解法
最直观的解法是使用O(n²)时间复杂度的动态规划:
- 定义dp数组,dp[i]表示以第i个数字结尾的最长上升子序列长度
- 初始化dp数组所有值为1,因为每个元素本身就是一个长度为1的子序列
- 对于每个i从1到n-1,遍历j从0到i-1:
- 如果nums[j] < nums[i],则dp[i] = max(dp[i], dp[j]+1)
- 最终结果是dp数组中的最大值
python复制def lengthOfLIS(nums):
if not nums:
return 0
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
1.2 优化解法:二分查找+贪心
更高效的O(nlogn)解法结合了贪心算法和二分查找:
- 维护一个tails数组,其中tails[i]表示长度为i+1的所有上升子序列的最小末尾元素
- 遍历nums中的每个数x:
- 如果x大于tails中所有元素,则将其追加到tails末尾
- 否则,用x替换tails中第一个大于等于x的元素
- 最终tails的长度就是LIS的长度
python复制import bisect
def lengthOfLIS(nums):
tails = []
for num in nums:
idx = bisect.bisect_left(tails, num)
if idx == len(tails):
tails.append(num)
else:
tails[idx] = num
return len(tails)
提示:二分查找解法虽然时间复杂度更优,但只能得到LIS的长度,无法直接得到具体的子序列。如果需要具体序列,还需要额外的处理步骤。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 最长上升子序列的常见变种
2.1 非严格递增版本
当题目允许子序列中相邻元素相等时,只需将比较条件从<改为<=即可:
python复制def lengthOfNonStrictLIS(nums):
if not nums:
return 0
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[j] <= nums[i]: # 仅修改了这一行
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
2.2 二维LIS问题
当元素是二维结构(如信封的宽度和高度)时,通常需要
