Lesson 158 of 164
Longest Arithmetic Progression
Given an array of integers, return the length of the longest arithmetic subsequence: elements taken in their original order (not necessarily adjacent) whose consecutive differences are all the same. The difference can be positive, negative, or zero.
LeetCode: 1027. Longest Arithmetic Subsequence
Example
[3, 6, 9, 12] -> 4 (difference 3)
[9, 4, 7, 2, 10] -> 3 (4, 7, 10)
[20, 1, 15, 3, 10, 5, 8] -> 4 (20, 15, 10, 5)Brute force: extend every pair
Any two elements i < j fix the first term and the difference d = nums[j] - nums[i]. Scan the rest of the array, and take each element equal to the next expected term.
public class Main {
static int longestAp(int[] nums) {
int n = nums.length;
int best = Math.min(n, 2);
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int d = nums[j] - nums[i];
int next = nums[j] + d;
int len = 2;
for (int k = j + 1; k < n; k++) {
if (nums[k] == next) {
len++;
next += d;
}
}
best = Math.max(best, len);
}
}
return best;
}
public static void main(String[] args) {
System.out.println(longestAp(new int[]{3, 6, 9, 12}) + " " + longestAp(new int[]{9, 4, 7, 2, 10}) + " " + longestAp(new int[]{20, 1, 15, 3, 10, 5, 8}));
}
}4 3 4Time: O(n³). Space: O(1).
Taking the first matching element greedily is safe: any later element with the same value could only continue the progression the same way, with fewer elements left after it.
Optimal: DP per index and difference
Let len[j][d] be the length of the longest arithmetic subsequence that ends at index j with difference d. For every earlier i, the element nums[j] extends the progression ending at i with difference d = nums[j] - nums[i]:
len[j][d] = len[i][d] + 1 (or 2, if no progression with difference d ends at i)Differences can be anything, so each index keeps a HashMap from difference to length. getOrDefault(d, 1) returns the stored length, or 1 when the key is absent (a single element is a progression of length 1). merge(d, extended, Math::max) stores the larger of the old and new value.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
static int longestAp(int[] nums) {
int n = nums.length;
List<Map<Integer, Integer>> len = new ArrayList<>();
for (int i = 0; i < n; i++) len.add(new HashMap<>());
int best = Math.min(n, 2);
for (int j = 0; j < n; j++) {
for (int i = 0; i < j; i++) {
int d = nums[j] - nums[i];
int extended = len.get(i).getOrDefault(d, 1) + 1;
int stored = len.get(j).merge(d, extended, Math::max);
best = Math.max(best, stored);
}
}
return best;
}
public static void main(String[] args) {
System.out.println(longestAp(new int[]{3, 6, 9, 12}) + " " + longestAp(new int[]{9, 4, 7, 2, 10}) + " " + longestAp(new int[]{20, 1, 15, 3, 10, 5, 8}));
System.out.println(longestAp(new int[]{5, 5, 5}) + " " + longestAp(new int[]{1}));
}
}4 3 4
3 1Time: O(n²) on average. Space: O(n²) in the worst case, one entry per pair.
With values in a small known range, an int[] indexed by d + offset is faster than a HashMap. nums[j] - nums[i] can overflow int for values near Integer.MIN_VALUE and Integer.MAX_VALUE; use long differences if the input allows such values.
Edge cases
- Fewer than two elements: the length of the array.
- Equal values: difference
0is a valid progression.
