Can Make Arithmetic Progression From Sequence
EasyPrompt
A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same.
Given an array of numbers arr, return true if the array can be rearranged to form an arithmetic progression. Otherwise, return false.
Example 1:
Input: arr = [3,5,1]
Output: true
Explanation: We can reorder the elements as [1,3,5] or [5,3,1] with differences 2 and -2 respectively, between each consecutive elements.Example 2:
Input: arr = [1,2,4]
Output: false
Explanation: There is no way to reorder the elements to obtain an arithmetic progression.
Constraints:
2 <= arr.length <= 1000-106 <= arr[i] <= 106
Approaches
3 approaches with complexity analysis and trade-offs.
To improve upon the O(N log N) time complexity of the sorting approach, we can use a HashSet to achieve a linear time solution. The core idea is to determine the parameters of the potential arithmetic progression (first term, last term, and common difference) in O(N) time, and then verify if all the required elements of this progression are present in the input array, also in O(N) time.
Algorithm
- Find the minimum (
minVal) and maximum (maxVal) elements inarr. - Store all unique elements of
arrin aHashSetfor efficient lookups. - The common difference of the AP must be
d = (maxVal - minVal) / (n - 1). If(maxVal - minVal)is not divisible by(n - 1), returnfalse. - If
d == 0, all elements must be the same. Check if theHashSetsize is 1. If not, returnfalse. - If
d != 0, all elements must be unique. Check if theHashSetsize isn. If not, returnfalse. - Iterate from
i = 0ton-1. For eachi, check if the expected termminVal + i * dexists in theHashSet. - If any expected term is missing, return
false. - If all terms are found, return
true.
Walkthrough
This approach avoids a full sort. Here's the breakdown:
- Find Parameters: We make one pass through the array to find the minimum (
minVal) and maximum (maxVal) elements. We also populate aHashSetwith all elements from the input array. The set helps in checking for the existence of elements inO(1)average time and also implicitly handles duplicates. - Calculate Difference: For an arithmetic progression of
nterms, the difference between the maximum and minimum term is(n-1) * diff. So, we can calculate the potential common differencediff = (maxVal - minVal) / (n - 1). IfmaxVal - minValis not perfectly divisible byn - 1, it's impossible to form an AP, so we returnfalse. - Handle Duplicates: If
diffis 0, it means all elements must be the same. This is a valid AP. We can verify this by checking if the size of ourHashSetis 1. Ifdiffis not 0, then all elements in an AP must be distinct. We verify this by checking if theHashSetsize is equal to the array lengthn. - Verify Progression: We now know the first term (
minVal) and the common difference (diff). The expected terms of the AP areminVal,minVal + diff,minVal + 2*diff, ...,maxVal. We can iteratentimes and for each stepi, check ifminVal + i * diffis present in ourHashSet. If any of these checks fail, we returnfalse. If all expected elements are found, we returntrue.
import java.util.HashSet;import java.util.Set; class Solution { public boolean canMakeArithmeticProgression(int[] arr) { int n = arr.length; if (n <= 2) { return true; } int minVal = Integer.MAX_VALUE; int maxVal = Integer.MIN_VALUE; Set<Integer> set = new HashSet<>(); for (int num : arr) { minVal = Math.min(minVal, num); maxVal = Math.max(maxVal, num); set.add(num); } if ((maxVal - minVal) % (n - 1) != 0) { return false; } int diff = (maxVal - minVal) / (n - 1); // If diff is 0, all elements must be the same. // The set size will be 1 if they are. if (diff == 0) { return set.size() == 1; } // If diff is not 0, all elements must be unique. // The set size must be n. if (set.size() != n) { return false; } for (int i = 0; i < n; i++) { if (!set.contains(minVal + i * diff)) { return false; } } return true; }}Complexity
Time
O(N)
Space
O(N)
Trade-offs
Pros
Achieves optimal O(N) time complexity.
Conceptually clear: determine the expected pattern and then verify its existence.
Cons
Requires O(N) extra space for the HashSet, which might be a concern for very large inputs under strict memory constraints.
This approach provides the most optimal solution with O(N) time and O(1) space complexity. It avoids both sorting and the use of an auxiliary data structure like a HashSet. The idea is to rearrange the array in-place, such that each element is moved to its correct position in the arithmetic progression. This is similar to the concept of Cycle Sort.
Algorithm
- Find
minValandmaxValinarr. - If
(maxVal - minVal)is not divisible by(n - 1), returnfalse. - Calculate
diff = (maxVal - minVal) / (n - 1). - If
diff == 0, check if all elements are equal. If so, returntrue, elsefalse. - Iterate through the array with an index
ifrom0ton-1. - For each
arr[i], if it's not at its correct sorted position (arr[i] != minVal + i * diff):- Calculate its target index
j = (arr[i] - minVal) / diff. - Check if
arr[i]is a valid AP number:(arr[i] - minVal) % diff == 0. If not, returnfalse. - Check for duplicates: if
arr[i] == arr[j], returnfalse. - Swap
arr[i]andarr[j]. - Repeat the process for the new
arr[i]without incrementingi(using an innerwhileloop).
- Calculate its target index
- If
arr[i]is already at its correct position, incrementi. - If the loop completes, return
true.
Walkthrough
The in-place manipulation method works by treating the array as a direct-mapped hash table.
-
First, just like the HashSet approach, we find the
minVal,maxVal, and the potential common differencediff. If the parameters are invalid (e.g.,maxVal - minValis not divisible byn-1), we returnfalseimmediately. -
The core of the algorithm is to place every number
xat its correct index. If the array were sorted as an AP, the elementxshould be at indexjsuch thatx = minVal + j * diff. This gives us the target indexj = (x - minVal) / diff. -
We iterate through the array from
i = 0ton-1. For eacharr[i], we check if it's already in its correct place. If not, we calculate its target indexj. We then swaparr[i]witharr[j]. After the swap,arr[j]is correct, but the newarr[i]might still be in the wrong place. We repeat this swap-and-check process for the current indexiuntil the elementarr[i]is correct. -
During this process, we must handle two failure conditions:
- Invalid Number: If we encounter a number
arr[i]that doesn't fit the AP pattern (i.e.,(arr[i] - minVal) % diff != 0), we can immediately returnfalse. - Duplicate Number: If we try to swap
arr[i]to its target positionj, but find thatarr[j]already has the same value (arr[i] == arr[j]), it implies a duplicate. Sincei != j, this is an invalid state for an AP with a non-zero difference, so we returnfalse.
- Invalid Number: If we encounter a number
If we can successfully place every element in its correct position, the array can form an AP.
class Solution { public boolean canMakeArithmeticProgression(int[] arr) { int n = arr.length; if (n <= 2) { return true; } int minVal = Integer.MAX_VALUE; int maxVal = Integer.MIN_VALUE; for (int num : arr) { minVal = Math.min(minVal, num); maxVal = Math.max(maxVal, num); } if (maxVal == minVal) { return true; // All elements are the same, diff is 0. } if ((maxVal - minVal) % (n - 1) != 0) { return false; } int diff = (maxVal - minVal) / (n - 1); int i = 0; while (i < n) { // If element is already in its correct place, move to the next. if (arr[i] == minVal + i * diff) { i++; } else { // Check if the number can be part of the AP. if ((arr[i] - minVal) % diff != 0) { return false; } int j = (arr[i] - minVal) / diff; // Check for duplicates. If arr[i] and arr[j] are the same, // it means we have a duplicate element because arr[i] is not // at its correct place (i != j). if (arr[i] == arr[j]) { return false; } // Swap arr[i] with arr[j] to place arr[j] correctly. int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } return true; }}Complexity
Time
O(N)
Space
O(1)
Trade-offs
Pros
Optimal time complexity of O(N).
Optimal space complexity of O(1) as it modifies the array in-place.
Cons
More complex to understand and implement correctly.
Modifies the input array, which might not be desirable in all scenarios.
The most straightforward approach is based on the definition of an arithmetic progression. An arithmetic progression is a sequence of numbers such that the difference between the consecutive terms is constant. If an array can be rearranged to form such a progression, then its sorted version must represent that progression (either in increasing or decreasing order). This leads to a simple algorithm: sort the array and then check if the difference between all adjacent elements is the same.
Algorithm
- Sort the input array
arrin non-decreasing order. - If the array can form an arithmetic progression, its sorted version must also be an arithmetic progression.
- Calculate the difference between the first two elements,
diff = arr[1] - arr[0]. - Iterate from the third element (
i = 2) to the end of the array. - For each element, check if the difference with its preceding element (
arr[i] - arr[i-1]) is equal todiff. - If any difference does not match, it's not an AP, so return
false. - If the loop completes without any mismatches, return
true.
Walkthrough
This method leverages the property that a sorted arithmetic progression has a constant difference between adjacent elements.
First, we sort the input array arr. This costs O(N log N) time. After sorting, if the original numbers can form an arithmetic progression, the sorted array [a_1, a_2, ..., a_n] must satisfy a_2 - a_1 = a_3 - a_2 = ... = a_n - a_{n-1}.
We can verify this by first calculating the common difference diff using the first two elements: diff = arr[1] - arr[0]. Then, we iterate through the rest of the array (from the third element onwards) and check if the difference between each element and its predecessor is equal to this diff. If we find any pair (arr[i], arr[i-1]) where arr[i] - arr[i-1] != diff, we can immediately conclude that it's not an arithmetic progression and return false. If the entire array is traversed without finding such a discrepancy, it confirms the array can form an arithmetic progression, and we return true.
import java.util.Arrays; class Solution { public boolean canMakeArithmeticProgression(int[] arr) { // An array with 2 or fewer elements can always form an AP. if (arr.length <= 2) { return true; } // Sort the array to check for a constant difference. Arrays.sort(arr); // Calculate the common difference from the first two elements. int diff = arr[1] - arr[0]; // Check if the rest of the elements follow the same difference. for (int i = 2; i < arr.length; i++) { if (arr[i] - arr[i - 1] != diff) { return false; } } return true; }}Complexity
Time
O(N log N)
Space
O(log N) or O(N)
Trade-offs
Pros
Very simple to understand and implement.
Robust and easy to debug.
Cons
The time complexity of O(N log N) is not optimal for this problem.
Solutions
Solution
class Solution { public boolean canMakeArithmeticProgression ( int [] arr ) { Arrays . sort ( arr ); int d = arr [ 1 ] - arr [ 0 ]; for ( int i = 2 ; i < arr . length ; ++ i ) { if ( arr [ i ] - arr [ i - 1 ] != d ) { return false ; } } return true ; } }/** * @param {number[]} arr * @return {boolean} */ var canMakeArithmeticProgression = function ( arr ) { arr . sort (( a , b ) => a - b ); for ( let i = 1 ; i < arr . length - 1 ; i ++ ) { if ( arr [ i ] << 1 != arr [ i - 1 ] + arr [ i + 1 ]) { return false ; } } return true ; };class Solution {public: bool canMakeArithmeticProgression(vector<int> &arr) { sort(arr.begin(), arr.end()); int d = arr[1] - arr[0]; for (int i = 2; i < arr.size(); i++) { if (arr[i] - arr[i - 1] != d) { return false; } } return true; }};class Solution : def canMakeArithmeticProgression ( self , arr : List [ int ]) -> bool : arr . sort () d = arr [ 1 ] - arr [ 0 ] return all ( b - a == d for a , b in pairwise ( arr ))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.