有没有遇到过这样的数字序列:
[3, 6, 9, 12]
你一看就知道:哇,这是一个节奏分明、步步递进的等差数列!
那么问题来了:**如果数组不是这么整齐呢?**比如:
ini
int[] arr = {3, 6, 9, 7, 12, 15, 4, 8};
请你找出这个数组中最长的等差子序列,也就是说,一段步伐一致的"节奏段子",长度最长是多少?
🧠 解题思路:把每一对数字看作"舞伴"
我们想象一下,一个数组里的每两个数字 array[j]
和 array[i]
(其中 j < i
)就是在跳舞。
只要这两个数字的"步伐差"(也就是 array[i] - array[j]
)一样,我们就可以把它们串成一段节奏。
于是我们用一个 Map<Integer, Integer>[] dp
数组来记录每一个以 i
结尾、以某个"步伐差值"跳起来的最长序列长度。
动态规划结构设计
ini
Map<Integer, Integer>[] dp = new Map[array.length];
dp[i]
表示以array[i]
结尾的等差子序列,Map
的键是差值diff
,值是长度。- 初始化每个
dp[i]
为一个新的 HashMap。
然后,我们从 i = 0
到 n - 1
,每次再枚举 j = 0
到 i - 1
,做如下事情:
差值计算:
c
int diff = array[i] - array[j];
🎯 状态转移:
- 如果
dp[j]
中没有这个差值,我们从头开始这段舞步(长度为2)。 - 否则,我们延续这个差值的节奏,长度 +1。
ini
if (!dp[j].containsKey(diff)) {
dp[i].put(diff, 2);
} else {
dp[i].put(diff, dp[j].get(diff) + 1);
}
更新全局最大值:
ini
globalMax = Math.max(globalMax, dp[i].get(diff));
最终代码
ini
import java.util.HashMap;
import java.util.Map;
public class LongestArithSequence {
public int longestArithSequence(int[] array){
if(array == null || array.length < 2) return 0;
int globalMax = 0;
Map<Integer, Integer>[] dp = new Map[array.length];
for(int i = 0; i < array.length; i++) {
dp[i] = new HashMap<>();
for(int j = 0; j < i; j++) {
int diff = array[i] - array[j];
dp[i].put(diff, dp[j].getOrDefault(diff, 1) + 1);
globalMax = Math.max(globalMax, dp[i].get(diff));
}
}
return globalMax;
}
public static void main(String[] args) {
LongestArithSequence las = new LongestArithSequence();
int[] array = {3, 6, 9, 7, 12, 15, 4, 8};
System.out.println("Longest arithmetic subsequence length: " + las.longestArithSequence(array));
}
}
🧩 小结:这道题考察了什么?
- ✅ 动态规划的状态定义能力
- ✅ 对"差值"这个维度的抽象能力
- ✅ 选择合适的数据结构(数组 + HashMap)来降低复杂度
思考
- 如果是等比数列,思路要怎么变?
- 如何打印出最长的那一段子序列?
- 如果数组非常长,比如上万条数据,如何进一步优化空间?