3731: Find Missing Elements
Problem Statement
Given an integer array nums of unique integers, find all missing integers in the range between the
smallest and largest elements. The smallest and largest elements are guaranteed to be present in the array.
Example 1
Input: nums = [1,4,2,5]
Output: [3]
Explanation: Range is [1,5], only 3 is missing.
Example 2
Input: nums = [7,8,6,9]
Output: []
Explanation: Range is [6,9], all numbers are present.
Example 3
Input: nums = [5,1]
Output: [2,3,4]
Explanation: Range is [1,5], missing 2, 3, and 4.
Key Insight
The solution relies on these crucial observations:
- The smallest and largest elements are always present in the array
- We only need to check numbers between min and max (inclusive)
- All integers in the array are unique
- The range is small (1 to 100), so even O(n²) approaches are efficient
Approach 1: Hash Set (Optimal)
Use a hash set to store all numbers for O(1) lookups, then iterate through the range to find missing numbers.
Algorithm Steps
- Find the minimum and maximum values in the array
- Store all numbers in a hash set for fast lookups
- Iterate from min to max (inclusive)
- Add numbers not found in the set to the result
- Return the result list
Complexity Analysis
| Time Complexity | Space Complexity |
|---|---|
| O(n) | O(n) |
Linear time for building set and checking range, linear space for the hash set.
class Solution {
public:
vector<int> findMissingElements(vector<int>& nums) {
unordered_set<int> numSet(nums.begin(), nums.end());
int minNum = *min_element(nums.begin(), nums.end());
int maxNum = *max_element(nums.begin(), nums.end());
vector<int> result;
for (int i = minNum; i <= maxNum; i++) {
if (numSet.find(i) == numSet.end()) {
result.push_back(i);
}
}
return result;
}
};
import java.util.*;
class Solution {
public List<Integer> findMissingElements(int[] nums) {
Set<Integer> numSet = new HashSet<>();
int minNum = Integer.MAX_VALUE;
int maxNum = Integer.MIN_VALUE;
for (int num : nums) {
numSet.add(num);
minNum = Math.min(minNum, num);
maxNum = Math.max(maxNum, num);
}
List<Integer> result = new ArrayList<>();
for (int i = minNum; i <= maxNum; i++) {
if (!numSet.contains(i)) {
result.add(i);
}
}
return result;
}
}
class Solution:
def findMissingElements(self, nums: List[int]) -> List[int]:
num_set = set(nums)
min_num = min(nums)
max_num = max(nums)
result = []
for i in range(min_num, max_num + 1):
if i not in num_set:
result.append(i)
return result
Approach 2: Sorting and Linear Scan
Sort the array and then scan through the range to find gaps where numbers are missing.
Algorithm Steps
- Sort the array in ascending order
- Find the minimum and maximum values (first and last elements after sorting)
- Use a pointer to track expected numbers in the range
- Iterate through sorted array and find missing numbers
- Return the result list
Complexity Analysis
| Time Complexity | Space Complexity |
|---|---|
| O(n log n) | O(1) |
Sorting dominates time complexity, constant space excluding output.
class Solution {
public:
vector<int> findMissingElements(vector<int>& nums) {
sort(nums.begin(), nums.end());
int minNum = nums[0];
int maxNum = nums.back();
vector<int> result;
int expected = minNum;
for (int num : nums) {
while (expected < num) {
result.push_back(expected);
expected++;
}
expected = num + 1;
}
return result;
}
};
import java.util.*;
class Solution {
public List<Integer> findMissingElements(int[] nums) {
Arrays.sort(nums);
int minNum = nums[0];
int maxNum = nums[nums.length - 1];
List<Integer> result = new ArrayList<>();
int expected = minNum;
for (int num : nums) {
while (expected < num) {
result.add(expected);
expected++;
}
expected = num + 1;
}
return result;
}
}
class Solution:
def findMissingElements(self, nums: List[int]) -> List[int]:
nums.sort()
min_num = nums[0]
max_num = nums[-1]
result = []
expected = min_num
for num in nums:
while expected < num:
result.append(expected)
expected += 1
expected = num + 1
return result
Approach 3: Boolean Array
Use a boolean array to mark presence of numbers in the range.
Algorithm Steps
- Find minimum and maximum values
- Create a boolean array of size (max - min + 1)
- Mark positions as true for numbers present in the array
- Collect indices that are false as missing numbers
- Return the result list
Complexity Analysis
| Time Complexity | Space Complexity |
|---|---|
| O(n) | O(range) |
Linear time, space proportional to the range between min and max.
class Solution {
public:
vector<int> findMissingElements(vector<int>& nums) {
int minNum = *min_element(nums.begin(), nums.end());
int maxNum = *max_element(nums.begin(), nums.end());
int rangeSize = maxNum - minNum + 1;
vector<bool> present(rangeSize, false);
for (int num : nums) {
present[num - minNum] = true;
}
vector<int> result;
for (int i = 0; i < rangeSize; i++) {
if (!present[i]) {
result.push_back(i + minNum);
}
}
return result;
}
};
class Solution {
public List<Integer> findMissingElements(int[] nums) {
int minNum = Integer.MAX_VALUE;
int maxNum = Integer.MIN_VALUE;
for (int num : nums) {
minNum = Math.min(minNum, num);
maxNum = Math.max(maxNum, num);
}
int rangeSize = maxNum - minNum + 1;
boolean[] present = new boolean[rangeSize];
for (int num : nums) {
present[num - minNum] = true;
}
List<Integer> result = new ArrayList<>();
for (int i = 0; i < rangeSize; i++) {
if (!present[i]) {
result.add(i + minNum);
}
}
return result;
}
}
class Solution:
def findMissingElements(self, nums: List[int]) -> List[int]:
min_num = min(nums)
max_num = max(nums)
range_size = max_num - min_num + 1
present = [False] * range_size
for num in nums:
present[num - min_num] = True
result = []
for i in range(range_size):
if not present[i]:
result.append(i + min_num)
return result
Edge Cases and Testing
1. No Missing Elements
Input: [1,2,3,4,5] → Output: []
Input: [6,7,8,9] → Output: []
2. All Elements Missing Except Ends
Input: [1,5] → Output: [2,3,4]
Input: [10,15] → Output: [11,12,13,14]
3. Single Missing Element
Input: [1,2,4,5] → Output: [3]
Input: [7,8,9,11] → Output: [10]
4. Random Missing Elements
Input: [1,3,5,7,9] → Output: [2,4,6,8]
Input: [2,5,8,11] → Output: [3,4,6,7,9,10]
5. Minimum Array Size
Input: [1,2] → Output: []
Input: [1,3] → Output: [2]
Detailed Explanation
The problem requires finding all integers that are missing between the smallest and largest elements of the array. The key insight is that we only need to consider numbers in the range [min, max] since the endpoints are guaranteed to be present.
For the input [1,4,2,5]:
- Minimum = 1, Maximum = 5
- Expected range: [1,2,3,4,5]
- Present: 1, 2, 4, 5
- Missing: 3
The solution approaches work as follows:
- Hash Set: Store all numbers in a set for O(1) lookups, then check each number in the range
- Sorting: Sort the array and find gaps between consecutive elements
- Boolean Array: Use array indices to mark presence of numbers
All approaches are efficient given the constraints (n ≤ 100, values ≤ 100), but the hash set approach is generally preferred for its simplicity and optimal time complexity.
FAQs
1. Why are the smallest and largest elements guaranteed to be present?
This is given in the problem statement: "The smallest and largest integers of the original range are still present in nums." This constraint simplifies the problem.
2. What is the time complexity of the hash set approach?
The hash set approach runs in O(n) time for building the set and O(range) time for checking numbers, giving overall O(n) time complexity.
3. What is the space complexity of the hash set approach?
The space complexity is O(n) for storing the hash set, which is efficient for the given constraints.
4. When would the boolean array approach be better?
The boolean array approach is better when the range between min and max is small, which is always true for this problem (max range = 100).
5. Can the array contain duplicate elements?
No, the problem states that the array consists of unique integers, so no duplicates exist.
6. What is the maximum possible range size?
Since 1 ≤ nums[i] ≤ 100, the maximum range size is 100 (from 1 to 100).
7. How does the sorting approach handle consecutive numbers?
In the sorting approach, when numbers are consecutive, the expected pointer moves smoothly without adding any missing numbers to the result.
8. What happens if the array is already sorted?
All approaches work correctly regardless of the initial order. The sorting approach would still sort it, but this is efficient for small n.
9. Can this problem be solved in O(1) space?
The sorting approach uses O(1) extra space (excluding output), but modifies the input array. If we cannot modify the input, we need O(n) space.
10. What is the most efficient approach for large ranges?
For large ranges, the hash set approach is most efficient as it doesn't depend on the range size for space, only on the number of elements.