LeetCode 1608 - Special Array With X Elements Greater Than or Equal X
The problem gives us an array nums containing non-negative integers. We need to determine whether there exists an intege
Difficulty: 🟢 Easy
Topics: Array, Binary Search, Sorting
Solution
Problem Understanding
The problem gives us an array nums containing non-negative integers. We need to determine whether there exists an integer x such that exactly x elements in the array are greater than or equal to x.
The important detail is that x does not need to appear inside the array itself. We are only concerned with the count of elements satisfying the condition.
For example, if nums = [3,5], then there are exactly two numbers greater than or equal to 2, namely 3 and 5. Therefore, the array is special and the answer is 2.
If no such integer exists, we return -1.
The constraints are relatively small:
1 <= nums.length <= 1000 <= nums[i] <= 1000
Since the array size is at most 100, even quadratic algorithms are acceptable. However, there is still value in designing a clean and efficient solution.
There are several edge cases that can easily cause mistakes:
- Arrays containing only zeros, such as
[0,0] - Arrays where multiple candidate values appear possible at first glance
- Arrays with repeated values
- Cases where the valid
xis not present in the array - Very small arrays, especially arrays of length
1
The problem guarantees that if a valid x exists, it is unique. This means we do not need to handle multiple valid answers.
Approaches
Brute Force Approach
The most direct solution is to try every possible value of x.
Since the array length is n, the number of elements greater than or equal to x can never exceed n. Therefore, we only need to test values from 0 to n.
For each candidate x:
- Traverse the array
- Count how many elements are greater than or equal to
x - Check whether the count equals
x
If we find a match, we return x. Otherwise, after testing all possibilities, we return -1.
This approach is correct because it explicitly checks the definition of a special array for every possible candidate.
The time complexity is O(n^2) because for each candidate x, we scan the entire array.
Optimal Approach
A better solution comes from sorting the array.
After sorting in ascending order, we can reason about how many elements are greater than or equal to a particular value.
Suppose the sorted array is:
a[0], a[1], a[2], ..., a[n-1]
At index i, there are exactly n - i elements from a[i] to the end of the array.
If:
a[i] >= n - i
then at least n - i elements are large enough.
However, we also need to ensure that exactly n - i elements satisfy the condition. That means the previous element must be smaller than n - i.
Formally:
a[i] >= n - i
and
a[i - 1] < n - i
If both conditions hold, then x = n - i.
Sorting allows us to compute the answer in a single linear scan after sorting.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | Tries every possible x and counts qualifying elements |
| Optimal | O(n log n) | O(1) or O(log n) | Sorts the array and uses index relationships |
Algorithm Walkthrough
Optimal Sorting Algorithm
- Sort the array in non-decreasing order.
Sorting groups smaller values at the beginning and larger values at the end. This makes it easy to determine how many elements are greater than or equal to a threshold.
2. Let n be the length of the array.
If we are currently examining index i, then there are exactly n - i elements from index i through the end of the array.
3. Iterate through the sorted array from left to right.
For each position i, compute:
candidate = n - i
This represents the number of elements remaining in the suffix. 4. Check whether the current value supports this candidate.
We need:
nums[i] >= candidate
because all elements from i onward must be at least candidate.
5. Ensure the previous element does not also satisfy the condition.
If i > 0, we also require:
nums[i - 1] < candidate
This guarantees that exactly candidate elements qualify, not more.
6. If both conditions are true, return candidate.
7. If the loop finishes without finding a valid candidate, return -1.
Why It Works
After sorting, the suffix starting at index i contains exactly n - i elements. If the smallest element in that suffix is at least n - i, then every element in the suffix also satisfies the condition.
The second condition ensures that the prefix elements do not accidentally increase the count beyond n - i. Together, these conditions guarantee that exactly x elements are greater than or equal to x.
Python Solution
from typing import List
class Solution:
def specialArray(self, nums: List[int]) -> int:
nums.sort()
n = len(nums)
for i in range(n):
candidate = n - i
if nums[i] >= candidate:
if i == 0 or nums[i - 1] < candidate:
return candidate
return -1
The implementation begins by sorting the array. Once sorted, every suffix of the array contains elements greater than or equal to the first value in that suffix.
The variable candidate represents the number of elements from the current index to the end of the array.
The first condition:
nums[i] >= candidate
checks whether all elements in the suffix are large enough.
The second condition:
i == 0 or nums[i - 1] < candidate
ensures that no earlier elements also qualify, which would increase the count beyond the candidate value.
If both conditions hold, we immediately return the candidate because the problem guarantees uniqueness.
If no valid candidate exists, the function returns -1.
Go Solution
package main
import "sort"
func specialArray(nums []int) int {
sort.Ints(nums)
n := len(nums)
for i := 0; i < n; i++ {
candidate := n - i
if nums[i] >= candidate {
if i == 0 || nums[i-1] < candidate {
return candidate
}
}
}
return -1
}
The Go implementation follows the exact same logic as the Python solution.
The primary difference is that Go uses sort.Ints(nums) to sort the slice in place. Slices in Go already behave like references to underlying arrays, so no additional copying is required.
There are no integer overflow concerns because the constraints are extremely small.
Worked Examples
Example 1
Input: nums = [3,5]
After sorting:
[3,5]
| i | nums[i] | candidate = n - i | nums[i] >= candidate | previous valid? | Result |
|---|---|---|---|---|---|
| 0 | 3 | 2 | Yes | i == 0 | Return 2 |
The answer is 2.
Example 2
Input: nums = [0,0]
After sorting:
[0,0]
| i | nums[i] | candidate | nums[i] >= candidate | Result |
|---|---|---|---|---|
| 0 | 0 | 2 | No | Continue |
| 1 | 0 | 1 | No | Continue |
No valid candidate exists, so we return -1.
Example 3
Input: nums = [0,4,3,0,4]
After sorting:
[0,0,3,4,4]
| i | nums[i] | candidate | nums[i] >= candidate | previous valid? | Result |
|---|---|---|---|---|---|
| 0 | 0 | 5 | No | - | Continue |
| 1 | 0 | 4 | No | - | Continue |
| 2 | 3 | 3 | Yes | 0 < 3 | Return 3 |
The answer is 3.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log n) | Sorting dominates the runtime |
| Space | O(1) or O(log n) | Depends on the sorting implementation |
The algorithm spends most of its time sorting the array. After sorting, the linear scan is only O(n).
Python's sorting algorithm may use additional stack space internally, while Go's sort implementation also uses some recursion stack space. No extra data structures proportional to the input size are created.
Test Cases
from typing import List
class Solution:
def specialArray(self, nums: List[int]) -> int:
nums.sort()
n = len(nums)
for i in range(n):
candidate = n - i
if nums[i] >= candidate:
if i == 0 or nums[i - 1] < candidate:
return candidate
return -1
sol = Solution()
assert sol.specialArray([3, 5]) == 2 # basic valid case
assert sol.specialArray([0, 0]) == -1 # all zeros
assert sol.specialArray([0, 4, 3, 0, 4]) == 3 # mixed values
assert sol.specialArray([1]) == 1 # single element valid
assert sol.specialArray([0]) == -1 # single zero invalid
assert sol.specialArray([3, 6, 7, 7, 0]) == -1 # no valid x
assert sol.specialArray([4, 4, 4, 4]) == 4 # all elements satisfy
assert sol.specialArray([0, 1, 2, 3, 4]) == 2 # valid x not in array
assert sol.specialArray([100]) == 1 # very large value
assert sol.specialArray([0, 0, 3, 3]) == 2 # repeated large values
assert sol.specialArray([2, 2, 2]) == -1 # count mismatch
assert sol.specialArray([5, 5, 5, 5, 5]) == 5 # maximum possible x
Test Summary
| Test | Why |
|---|---|
[3,5] |
Basic example with valid answer |
[0,0] |
No valid x exists |
[0,4,3,0,4] |
Mixed values with duplicates |
[1] |
Smallest valid array |
[0] |
Smallest invalid array |
[3,6,7,7,0] |
No candidate satisfies condition |
[4,4,4,4] |
Every element contributes |
[0,1,2,3,4] |
Valid x does not appear in array |
[100] |
Large number with small array |
[0,0,3,3] |
Duplicate qualifying elements |
[2,2,2] |
Candidate count mismatch |
[5,5,5,5,5] |
x equals array length |
Edge Cases
One important edge case is an array containing only zeros, such as [0,0]. A naive implementation might incorrectly think x = 0 works because no elements are greater than or equal to a positive number. However, when x = 0, every element is greater than or equal to 0, so the count becomes the full array length instead of zero. The implementation correctly handles this by explicitly checking the exact count condition.
Another important case is when the valid x does not appear in the array itself. For example, in [0,1,2,3,4], the answer is 2, even though 2 is only one of several values. The algorithm never assumes that x must exist in the array. Instead, it derives candidates from the number of remaining elements after sorting.
A third edge case involves duplicate values near the boundary. Consider [0,0,3,3]. Here, exactly two elements are greater than or equal to 2. The second condition:
nums[i - 1] < candidate
is essential because it prevents overcounting when repeated values appear. Without this check, the algorithm could incorrectly accept candidates where more than x elements satisfy the condition.
A final subtle case occurs when every element qualifies, such as [5,5,5,5,5]. In this situation, the valid answer is the array length itself. The implementation correctly handles this because the first index naturally represents a suffix containing the entire array.