1.题目链接:
2.题目描述:
给你一个整数数组 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
3. 解法(动态规划):
算法思路:
1. 状态表示:
对于线性 dp ,我们可以用「经验 + 题目要求」来定义状态表示:
i. | 以某个位置为结尾,巴拉巴拉; |
ii. 以某个位置为起点,巴拉巴拉。
这里我们选择比较常用的方式,以某个位置为结尾,结合题目要求,定义一个状态表示:dp[i] 表示:以 i 位置元素为结尾的「所有子序列」中,最长的等差序列的长度。
但是这里有一个非常致命的问题,那就是我们无法确定 i 结尾的等差序列的样子。这样就会导致我们无法推导状态转移方程,因此我们定义的状态表示需要能够确定一个等差序列。
根据等差序列的特性,我们仅需知道序列里面的最后两个元素,就可以确定这个序列的样子。因此,我们修改我们的状态表示为:
dp[i][j] 表示:以 i 位置以及 j 位置的元素为结尾的所有的子序列中,最长的等差序列的长度。规定一下 i < j 。
2. 状态转移方程:
设 nums[i] = b, nums[j] = c ,那么这个序列的前一个元素就是 a = 2 * b - c 。我们根据 a 的情况讨论:
a. | a 存在,下标为 k ,并且 a < b :此时我们需要以 k 位置以及 i 位置元素为结尾的最 |
长等差序列的长度,然后再加上 j 位置的元素即可。于是 dp[i][j] = dp[k][i] + 1 。这里因为会有许多个 k ,我们仅需离 i 最近的 k 即可。因此任何最长的都可以以 k为结尾;
b. | a 存在,但是 b < a < c :此时只能两个元素自己玩了,dp[i][j] = 2 ; |
c. | a 不存在:此时依旧只能两个元素自己玩了,dp[i][j] = 2 。 |
综上,状态转移方程分情况讨论即可。
优化点:我们发现,在状态转移方程中,我们需要确定 a 元素的下标。因此我们可以将所有的元素 + 下标绑定在一起,放到哈希表中,这里有两种策略:
a. 在 dp 之前,放入哈希表中。这是可以的,但是需要将下标形成一个数组放进哈希表中。这样 时间复杂度较高,我帮大家试过了,超时。
b. 一边 dp ,一边保存。这种方式,我们仅需保存最近的元素的下标,不用保存下标数组。但是用这种方法的话,我们在遍历顺序那里,先固定倒数第二个数,再遍历倒数第一个数。这样就可以在 i 使用完时候,将 nums[i] 扔到哈希表中。
3. 初始化:
根据实际情况,可以将所有位置初始化为 2 。
4. 填表顺序:
a. 先固定倒数第二个数;
b. 然后枚举倒数第一个数。
5. 返回值:
由于不知道最长的结尾在哪里,因此返回 dp 表中的最大值。
Java算法代码:
class Solution {
public int longestArithSeqLength(int[] nums) {
Map<Integer, Integer> hash = new HashMap<Integer, Integer>();
hash.put(nums[0], 0);
int n = nums.length;
int[][] dp = new int[n][n];
for( int i = 0; i < n; i++){
Arrays.fill(dp[i], 2);
}
int ret = 2;
for(int i = 1; i < n; i++){
for(int j = i + 1; j < n; j++){
int a = 2 * nums[i] - nums[j];
if(hash.containsKey(a)){
dp[i][j] = dp[hash.get(a)][i] + 1;
ret = Math.max(ret, dp[i][j]);
}
}
hash.put(nums[i], i);
}
return ret;
}
}
运行结果:
动态规划: