问题描述
给定一个整数数组 A,返回A 中最长等差子序列的长度。
输入:[20,1,15,3,10,5,8]
输出:4
解释:最长的等差子序列是 [20,15,10,5]
问题分析
我们要找出最长的等差子序列,则必须要找出所有的等差子序列,然后比较它们的长度,找出最长的。那么,怎么找,才能保证不重复,不遗漏呢?如果你使用动态规划,做过最大子序列和的问题,那么很容易产生灵感,我们可以依次寻找以数组A[i](0=
如何由A[i-1]结尾的所有等差子序列,求出以A[i]结尾的所有等差子序列呢?仔细想想,好像以A[i]结尾的等差子序列,并不见得与以A[i-1]结尾的等差子序列,有多大关系呀?事实也确实如此,A[i]的子序列,甚至都不需要A[i-1]的参与,随便举个例子,4,6,9,8,以8结尾的等差子序列4,6,8与以9结尾的等差子序列6,9,一点关系都没有。A[i]与A[i-1]之所以没有明显的关系,是因为这里的等差子序列,并不要求是连续的,而是可以跳跃存在的。事实上,要求出A[i]结尾的等差子序列,则必须一一算出A[i]与A[0...i-1]的差值,分别与以A[0...i-1]结尾的所有等差子序列的差值作比较,若相等,则以这个差值为key,以相等的这个等差子序列的长度+1作为value,挂到A[i]名下;若不等,那就以差值为key,长度2为value丢到A[i]脚下。如果A[i]那里已经有这个key了,那还得比较新的value与原始value的大小,将大的存进去。在递推的过程中,每次更新A[i]的等差子序列的长度时,顺便更新一下我们要的结果,也就是最长等差子序列的长度,循环结束后,返回这个最长等差子序列的长度,即可。
代码实现
@Test
public void testAdditiveNumber() {
int[] A = {9,37,30,50,67,32,47,44,11,15,4,11,3,12,17,18,25,19,21,17,21,23,70,51,61,21,52,25};
System.out.println("Result: "+new Solution().
longestArithSeqLength(A));
}
class Solution {
public int longestArithSeqLength(int[] A) {
if(A == null){
return 0;
}else if(A.length < 3){
return A.length;
}
int n = A.length;
//以A[i]结尾的所有等差数列的差值和长度 HashMap> diffMap = new HashMap<>();
for(int i=0; i
diffMap.put(i, new HashMap());
}
diffMap.get(0).put(0, 1);
diffMap.get(1).put(A[1] - A[0], 2);
int result = 2;
for(int i=2; i
for(int j=i-1; j>=0; j--) {
//求a[i]与A[0...i-1]的差值 int diff = A[i] - A[j];
//以A[j]结尾的所有等差数列中,存在与A[i]-A[j]相等的差值 if(diffMap.get(j).containsKey(diff)){
//更新以A[i]结尾的等差数列的差值和对应的长度 if(diffMap.get(i).get(diff) == null) {
diffMap.get(i).put(diff, diffMap.get(j).get(diff) + 1);
}else{
diffMap.get(i).put(diff, Math.max(diffMap.get(j).get(diff) + 1, diffMap.get(i).get(diff)));
}
}else{
if(diffMap.get(i).get(diff) == null) {
diffMap.get(i).put(diff, 2);
}
}
//更新最长等差数列的长度 result = Math.max(result, diffMap.get(i).get(diff));
}
}
return result;
}
}