194 lines
8.2 KiB
YAML
194 lines
8.2 KiB
YAML
title: Can Make Arithmetic Progression From Sequence
|
|
slug: can-make-arithmetic-progression-from-sequence
|
|
difficulty: easy
|
|
leetcode_id: 1502
|
|
leetcode_url: https://leetcode.com/problems/can-make-arithmetic-progression-from-sequence/
|
|
categories:
|
|
- arrays
|
|
- sorting
|
|
patterns:
|
|
- two-pointers
|
|
|
|
function_signature: "def can_make_arithmetic_progression(arr: list[int]) -> bool:"
|
|
|
|
test_cases:
|
|
visible:
|
|
- input: { arr: [3, 5, 1] }
|
|
expected: true
|
|
- input: { arr: [1, 2, 4] }
|
|
expected: false
|
|
hidden:
|
|
- input: { arr: [1, 2] }
|
|
expected: true
|
|
- input: { arr: [5, 5, 5, 5] }
|
|
expected: true
|
|
- input: { arr: [0, 0] }
|
|
expected: true
|
|
- input: { arr: [-1, -5, -3] }
|
|
expected: true
|
|
- input: { arr: [1, 100] }
|
|
expected: true
|
|
- input: { arr: [1, 2, 3, 4, 6] }
|
|
expected: false
|
|
- input: { arr: [10, 5, 0, -5, -10] }
|
|
expected: true
|
|
|
|
description: |
|
|
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`.
|
|
|
|
constraints: |
|
|
- `2 <= arr.length <= 1000`
|
|
- `-10^6 <= arr[i] <= 10^6`
|
|
|
|
examples:
|
|
- 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."
|
|
- input: "arr = [1,2,4]"
|
|
output: "false"
|
|
explanation: "There is no way to reorder the elements to obtain an arithmetic progression."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Think of an arithmetic progression like evenly spaced fence posts — each post is exactly the same distance from its neighbours. When you're given a jumbled pile of posts, you need to check if they *could* be arranged with equal spacing.
|
|
|
|
The key insight is that once you **sort** the array, an arithmetic progression will reveal itself naturally. In a sorted AP, every consecutive pair must have the same difference. If even one pair differs, the answer is `false`.
|
|
|
|
Why does sorting work? Because an AP is uniquely determined by its smallest element and its common difference. Sorting puts elements in order, and if an AP exists, the sorted array *is* that AP. You just need to verify the spacing is consistent.
|
|
|
|
An alternative mathematical insight: in a valid AP, we can compute the expected common difference as `(max - min) / (n - 1)`. Every element should then be exactly at position `min + i * diff` for some index `i`. This leads to an O(n) solution using a hash set.
|
|
|
|
approach: |
|
|
We present the **Sorting Approach** as the primary solution, which is intuitive and efficient for this problem size.
|
|
|
|
**Step 1: Handle the trivial case**
|
|
|
|
- If the array has only 2 elements, it's always an AP (any two numbers form an AP)
|
|
- Return `true` immediately
|
|
|
|
|
|
|
|
**Step 2: Sort the array**
|
|
|
|
- Sort `arr` in ascending order
|
|
- This arranges elements so that if an AP exists, consecutive elements will be adjacent
|
|
|
|
|
|
|
|
**Step 3: Calculate the common difference**
|
|
|
|
- Compute `diff = arr[1] - arr[0]` — this is what every consecutive pair must equal
|
|
|
|
|
|
|
|
**Step 4: Verify all consecutive pairs**
|
|
|
|
- Iterate through the sorted array from index `1` to `n-1`
|
|
- For each index `i`, check if `arr[i] - arr[i-1] == diff`
|
|
- If any pair differs, return `false`
|
|
|
|
|
|
|
|
**Step 5: Return the result**
|
|
|
|
- If all pairs have the same difference, return `true`
|
|
|
|
common_pitfalls:
|
|
- title: Forgetting to Sort First
|
|
description: |
|
|
A common mistake is checking differences on the **unsorted** array. The problem says the array *can be rearranged*, so order doesn't matter for input.
|
|
|
|
For example, `[3, 5, 1]` has differences `[2, -4]` in its original order — not constant! But sorted as `[1, 3, 5]`, the difference is consistently `2`.
|
|
wrong_approach: "Check consecutive differences without sorting"
|
|
correct_approach: "Sort first, then check differences"
|
|
|
|
- title: Integer Division Pitfall (O(n) approach)
|
|
description: |
|
|
When using the O(n) formula-based approach, the common difference is `(max - min) / (n - 1)`. If this doesn't divide evenly, there's no valid AP.
|
|
|
|
For example, with `[1, 2, 4]`: `max=4`, `min=1`, `n=3`, so `diff = 3 / 2 = 1.5`. Since we need integer positions, this signals `false`. Forgetting this check leads to incorrect results.
|
|
wrong_approach: "Use integer division without checking remainder"
|
|
correct_approach: "Check if (max - min) % (n - 1) == 0 before proceeding"
|
|
|
|
- title: Not Handling Duplicates Correctly
|
|
description: |
|
|
When all elements are the same (e.g., `[5, 5, 5]`), the common difference is `0`. This is a valid AP! Some solutions incorrectly reject this case.
|
|
|
|
In the sorting approach, this works automatically since `arr[i] - arr[i-1] = 0` for all pairs.
|
|
wrong_approach: "Assuming diff must be non-zero"
|
|
correct_approach: "Allow diff = 0 for arrays with all identical elements"
|
|
|
|
key_takeaways:
|
|
- "**Sorting reveals structure**: When order doesn't matter in the input, sorting often simplifies the problem by imposing a predictable structure"
|
|
- "**AP properties**: An arithmetic progression is fully determined by its first term and common difference — use this to validate"
|
|
- "**Trade-off awareness**: O(n log n) sorting is simple and sufficient here, but O(n) is achievable with hash sets when needed"
|
|
- "**Edge cases matter**: Arrays of length 2 and arrays with all identical elements are valid APs"
|
|
|
|
time_complexity: "O(n log n). Dominated by the sorting step. The subsequent linear scan is O(n)."
|
|
space_complexity: "O(1) or O(n) depending on sorting implementation. In-place sorts like heapsort use O(1) extra space, while Python's Timsort uses O(n)."
|
|
|
|
solutions:
|
|
- approach_name: Sorting
|
|
is_optimal: true
|
|
code: |
|
|
def can_make_arithmetic_progression(arr: list[int]) -> bool:
|
|
# Sort to arrange elements in order
|
|
arr.sort()
|
|
|
|
# Calculate the expected common difference
|
|
diff = arr[1] - arr[0]
|
|
|
|
# Check all consecutive pairs have the same difference
|
|
for i in range(2, len(arr)):
|
|
if arr[i] - arr[i - 1] != diff:
|
|
return False
|
|
|
|
return True
|
|
explanation: |
|
|
**Time Complexity:** O(n log n) — Sorting dominates; the linear scan is O(n).
|
|
|
|
**Space Complexity:** O(1) to O(n) — Depends on sorting algorithm (in-place vs. not).
|
|
|
|
This approach is clean and intuitive. By sorting, we transform the problem into simply verifying that adjacent differences are constant. The constraint `n <= 1000` makes O(n log n) more than sufficient.
|
|
|
|
- approach_name: Hash Set (O(n) Time)
|
|
is_optimal: false
|
|
code: |
|
|
def can_make_arithmetic_progression(arr: list[int]) -> bool:
|
|
n = len(arr)
|
|
min_val = min(arr)
|
|
max_val = max(arr)
|
|
|
|
# If all elements are the same, it's a valid AP with diff=0
|
|
if max_val == min_val:
|
|
return True
|
|
|
|
# Check if common difference would be an integer
|
|
if (max_val - min_val) % (n - 1) != 0:
|
|
return False
|
|
|
|
diff = (max_val - min_val) // (n - 1)
|
|
|
|
# Use a set to check each element is at a valid AP position
|
|
seen = set()
|
|
for num in arr:
|
|
# Each element should be at position: min + k * diff
|
|
# So (num - min) should be divisible by diff
|
|
if (num - min_val) % diff != 0:
|
|
return False
|
|
|
|
# Check for duplicates (other than when diff=0)
|
|
if num in seen:
|
|
return False
|
|
seen.add(num)
|
|
|
|
return True
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through array after finding min/max.
|
|
|
|
**Space Complexity:** O(n) — Hash set stores all elements.
|
|
|
|
This approach avoids sorting by using mathematical properties of APs. In a valid AP, every element must be exactly at `min + k * diff` for some integer `k` in `[0, n-1]`. We verify this using a hash set to also detect duplicates. While asymptotically faster, the sorting approach is often preferred for its simplicity.
|