线性动态规划:最长等差数列
字数 1650 2025-10-26 09:00:43
线性动态规划:最长等差数列
题目描述
给定一个整数数组 nums,返回数组中最长等差数列的长度。数组中等差数列的定义是:至少包含 3 个元素,且任意两个相邻元素的差相等。数组元素的顺序不能改变。
示例:
- 输入:
nums = [3,6,9,12]
输出:4
解释:整个数组就是公差为 3 的等差数列。 - 输入:
nums = [9,4,7,2,10]
输出:3
解释:最长的等差数列是[4,7,10]或[9,7,5](但 5 不在数组中,实际是[9,7,5]不成立,正确应为[4,7,10]公差为 3)。
解题思路
本题的关键是:等差数列可以由任意两个元素确定一个公差。如果我们枚举所有可能的公差,并记录以某个位置结尾的等差数列长度,就能用动态规划解决。
步骤 1:定义状态
设 dp[i][d] 表示以 nums[i] 结尾、公差为 d 的等差数列的最大长度。但由于公差可能很大,我们通常用哈希表来存储公差:
- 定义
dp[i]为一个哈希表,键是公差d,值是以nums[i]结尾、公差为d的等差数列长度。
步骤 2:状态转移
对于每个 i(从 0 到 n-1),遍历所有 j(从 0 到 i-1):
- 计算公差
d = nums[i] - nums[j]。 - 如果
dp[j]中存在公差d,说明在j之前已经有以d为公差的数列,那么接上nums[i]后长度加 1:
dp[i][d] = dp[j][d] + 1 - 如果
dp[j]中没有公差d,说明nums[j]和nums[i]是这个等差数列的前两项,此时长度为 2(但题目要求至少 3 个元素,所以长度为 2 时不更新答案)。
步骤 3:初始化
每个单独的元素可以视为任何公差的等差数列(长度为 1),但初始时所有 dp[i] 的哈希表为空。
步骤 4:记录答案
在遍历过程中,记录所有 dp[i][d] 的最大值,最终返回这个最大值(如果最大值小于 3,则返回 0,但题目通常保证至少有一个答案)。
详细推导(以 nums = [9,4,7,2,10] 为例)
- i=0:
dp[0]为空(只有一个元素,无法构成等差数列)。 - i=1:
- j=0:d = 4-9 = -5,
dp[1][-5] = 2(因为dp[0]没有 -5,所以初始为 2)。
- j=0:d = 4-9 = -5,
- i=2:
- j=0:d = 7-9 = -2,
dp[2][-2] = 2。 - j=1:d = 7-4 = 3,
dp[2][3] = 2。
- j=0:d = 7-9 = -2,
- i=3:
- j=0:d = 2-9 = -7,
dp[3][-7] = 2。 - j=1:d = 2-4 = -2,此时
dp[1]没有 -2,所以dp[3][-2] = 2。 - j=2:d = 2-7 = -5,此时
dp[2]没有 -5,所以dp[3][-5] = 2。
- j=0:d = 2-9 = -7,
- i=4:
- j=0:d = 10-9 = 1,
dp[4][1] = 2。 - j=1:d = 10-4 = 6,
dp[4][6] = 2。 - j=2:d = 10-7 = 3,关键点:
dp[2]中有公差 3(对应数列 [4,7]),所以dp[4][3] = dp[2][3] + 1 = 3,此时数列为 [4,7,10]。 - j=3:d = 10-2 = 8,
dp[4][8] = 2。
- j=0:d = 10-9 = 1,
最终最大值为 3。
复杂度优化
直接使用二维哈希表可能会占用较大空间,但由于每个 dp[i] 只依赖于前面的 dp[j],空间复杂度为 O(n²)(最坏情况公差不同),时间复杂度也是 O(n²)。
代码框架(伪代码)
def longestArithSeqLength(nums):
n = len(nums)
dp = [dict() for _ in range(n)]
ans = 2 # 至少两个元素才能开始
for i in range(n):
for j in range(i):
d = nums[i] - nums[j]
if d in dp[j]:
dp[i][d] = dp[j][d] + 1
else:
dp[i][d] = 2
ans = max(ans, dp[i][d])
return ans
总结
本题通过“结尾元素 + 公差”确定状态,利用哈希表避免枚举所有可能的公差,是线性动态规划中灵活使用数据结构的典型例子。