给你一个整数数组 nums
,返回 nums
中最长等差子序列的长度。
回想一下,nums
的子序列是一个列表 nums[i1], nums[i2], ..., nums[ik]
,且 0 <= i1 < i2 < ... < ik <= nums.length - 1
。并且如果 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 <= 1000
0 <= nums[i] <= 500
class Solution {
public int longestArithSeqLength(int[] nums) {
// dp[i][j] 表示公差为j , 等差数列的最长长度
int max=nums[0],min=max;
for(int a:nums){
max = Math.max(a,max);
min = Math.min(a,min);
}
int[][]dp=new int[nums.length+1][(max-min+1)*2];
for(int[]a:dp){
Arrays.fill(a,1);
}
int res=0;
for(int i=0;i<nums.length;i++){
for(int k=0;k<i;k++){
// 公差
int val = nums[i]-nums[k] + (max-min);
dp[i][val]=Math.max(
dp[i][val],
dp[k][val]+1);
res=Math.max(res,dp[i][val]);
}
}
return res;
}
}
看到这种当前状态跟前一种情况有关的题目 , 其实可以先往DP的思路上面靠拢靠拢.
那么一般的, 当我们看到一道DP的题目 , 首先该思考 DP数组怎么定义
比如本题需要 找 等差数列, 并且题目给出的数组都是无序的 (并且序列的顺序我们不能打乱)
这里很容易想到 dp[i] 是需要的, 那么对于另一个关键点 ---- 公差 , 我们再记录一个数组进行统计 ,
也就得出了dp数组的定义 : dp[i][j]
表示 下标为 i 的元素 , 公差为j时候 , 等差数列的最长长度
接着是初始化 , 对于任何一个元素 , 都可以成为一个长度为1 的等差数列, 因此 我们初始化DP数组的所有元素为1 ,
这个时候需要注意题目给出的数据范围 : 0 <= nums[i] <= 500
那么本题公差的范围就是 [-500,500]
, 这里也可以先统计一下 最大值和最小值来 进一步节约内存空间
在遍历每一个元素的时候我们统计 当前元素 与 之前的元素可以构成的等差数列的长度
int val = nums[i]-nums[k] + (max-min); dp[i][val]=Math.max( dp[i][val], dp[k][val]+1); res=Math.max(res,dp[i][val]);
这里注意 , 由于 数组的索引是没有负数的, 因此我们定义两倍的 长度 , 在每次计算索引的时候 加上 最大公差的偏移量即可
在计算的过程中维护 res 来统计 最长的等差数列的长度即可