Medium
题目描述
给你一个整数数组 nums,返回 nums 中最长等差子序列的长度。
注意:
- 子序列是一个可以从另一个数组派生出来的数组,通过删除某些或不删除元素而不改变其余元素的顺序。
- 如果
seq[i + 1] - seq[i]的值都相同(对于0 <= i < seq.length - 1),则序列seq是等差的。
示例 1:
输入: nums = [3,6,9,12]
输出: 4
解释: 整个数组是一个长度为 3 的等差数列。
示例 2:
输入: nums = [9,4,7,2,10]
输出: 3
解释: 最长的等差子序列是 [4,7,10]。
示例 3:
输入: nums = [20,1,15,3,10,5,8]
输出: 4
解释: 最长的等差子序列是 [20,15,10,5]。
提示:
2 <= nums.length <= 10000 <= nums[i] <= 500
解题思路
这是一道典型的动态规划题目,关键是找到状态转移的方法。
核心思路: 对于每个位置 i,我们需要考虑以 nums[i] 结尾的所有可能等差数列。由于等差数列的性质,我们可以用公差来唯一确定一个等差数列的特征。
状态定义:
使用 dp[i][d] 表示以 nums[i] 结尾、公差为 d 的等差数列的最大长度。
状态转移:
对于每个位置 i,我们遍历之前的所有位置 j(j < i),计算公差 d = nums[i] - nums[j]。如果之前存在以 nums[j] 结尾、公差为 d 的等差数列,那么我们可以将 nums[i] 添加到该数列末尾,长度加 1。
实现细节:
- 由于公差可能为负数,我们需要对公差进行偏移处理(加上一个偏移量)
- 每个等差数列至少有 2 个元素,所以初始长度为 2
- 最终答案是所有 dp[i][d] 中的最大值
时间复杂度优化: 可以使用哈希表来存储状态,避免数组的空间浪费,特别是当数值范围很大时。
代码实现
class Solution {
public:
int longestArithSeqLength(vector<int>& nums) {
int n = nums.size();
if (n <= 2) return n;
vector<vector<int>> dp(n, vector<int>(1001, 1));
int maxLen = 2;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
int diff = nums[i] - nums[j] + 500; // 偏移500避免负索引
dp[i][diff] = dp[j][diff] + 1;
maxLen = max(maxLen, dp[i][diff]);
}
}
return maxLen;
}
};
class Solution:
def longestArithSeqLength(self, nums: List[int]) -> int:
n = len(nums)
if n <= 2:
return n
dp = [{}] * n
for i in range(n):
dp[i] = {}
max_len = 2
for i in range(1, n):
for j in range(i):
diff = nums[i] - nums[j]
dp[i][diff] = dp[j].get(diff, 1) + 1
max_len = max(max_len, dp[i][diff])
return max_len
public class Solution {
public int LongestArithSeqLength(int[] nums) {
int n = nums.Length;
if (n <= 2) return n;
var dp = new Dictionary<int, int>[n];
for (int i = 0; i < n; i++) {
dp[i] = new Dictionary<int, int>();
}
int maxLen = 2;
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
int diff = nums[i] - nums[j];
dp[i][diff] = dp[j].GetValueOrDefault(diff, 1) + 1;
maxLen = Math.Max(maxLen, dp[i][diff]);
}
}
return maxLen;
}
}
var longestArithSeqLength = function(nums) {
const n = nums.length;
if (n <= 2) return n;
const dp = Array(n).fill(null).map(() => new Map());
let maxLen = 2;
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
const diff = nums[i] - nums[j];
dp[i].set(diff, (dp[j].get(diff) || 1) + 1);
maxLen = Math.max(maxLen, dp[i].get(diff));
}
}
return maxLen;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 需要遍历所有位置对(i,j),其中i>j |
| 空间复杂度 | O(n²) | 在最坏情况下,每个位置可能存储O(n)个不同的公差 |
相关题目
- . Destroy Sequential Targets (Medium)