Recover the Original Array
HardPrompt
Alice had a 0-indexed array arr consisting of n positive integers. She chose an arbitrary positive integer k and created two new 0-indexed integer arrays lower and higher in the following manner:
lower[i] = arr[i] - k, for every indexiwhere0 <= i < nhigher[i] = arr[i] + k, for every indexiwhere0 <= i < n
Unfortunately, Alice lost all three arrays. However, she remembers the integers that were present in the arrays lower and higher, but not the array each integer belonged to. Help Alice and recover the original array.
Given an array nums consisting of 2n integers, where exactly n of the integers were present in lower and the remaining in higher, return the original array arr. In case the answer is not unique, return any valid array.
Note: The test cases are generated such that there exists at least one valid array arr.
Example 1:
Input: nums = [2,10,6,4,8,12]
Output: [3,7,11]
Explanation:
If arr = [3,7,11] and k = 1, we get lower = [2,6,10] and higher = [4,8,12].
Combining lower and higher gives us [2,6,10,4,8,12], which is a permutation of nums.
Another valid possibility is that arr = [5,7,9] and k = 3. In that case, lower = [2,4,6] and higher = [8,10,12]. Example 2:
Input: nums = [1,1,3,3]
Output: [2,2]
Explanation:
If arr = [2,2] and k = 1, we get lower = [1,1] and higher = [3,3].
Combining lower and higher gives us [1,1,3,3], which is equal to nums.
Note that arr cannot be [1,3] because in that case, the only possible way to obtain [1,1,3,3] is with k = 0.
This is invalid since k must be positive.Example 3:
Input: nums = [5,435]
Output: [220]
Explanation:
The only possible combination is arr = [220] and k = 215. Using them, we get lower = [5] and higher = [435].
Constraints:
2 * n == nums.length1 <= n <= 10001 <= nums[i] <= 109- The test cases are generated such that there exists at least one valid array
arr.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach explores all possible ways to partition the 2n numbers into a lower array and a higher array, each of size n. For each partition, it checks if it could have been generated from some original array arr and a positive integer k.
Algorithm
- Generate all possible subsets of
numsthat have a size ofn. Each such subset is a candidate for thelowerarray. - For each candidate
lowerarray, the remainingnelements ofnumsform the candidatehigherarray. - Sort both the candidate
lowerandhigherarrays. - Calculate the difference
d = higher[0] - lower[0]. - If
dis not a positive value, this partition is invalid, so we discard it and move to the next one. - Check if this difference
dis consistent for all pairs of elements, i.e.,higher[i] - lower[i] == dfor alli. - If the difference is consistent, we have found a valid
2k = d. The original arrayarrcan be reconstructed asarr[i] = lower[i] + k(wherek = d / 2). - Since a solution is guaranteed to exist, this process will eventually find it, and we can return the reconstructed
arr.
Walkthrough
The most straightforward, yet impractical, way to solve the problem is to try every possible assignment of the 2n numbers in nums to either the lower set or the higher set. The number of ways to choose n elements for the lower set from 2n elements is given by the binomial coefficient "2n choose n" (C(2n, n)).
The algorithm would recursively generate all subsets of nums of size n. Each such subset is a candidate for the lower array. The remaining n elements would form the higher array.
For each candidate partition (lower_candidate, higher_candidate):
- Sort both
lower_candidateandhigher_candidatearrays. - Calculate the difference
d = higher_candidate[0] - lower_candidate[0]. - Check if this difference
dis positive and consistent for all other pairs of elements. That is, check ifhigher_candidate[i] - lower_candidate[i] == dfor allifrom1ton-1. - If the difference is consistent and positive, we have found a valid
2k = d. The original arrayarrcan be reconstructed asarr[i] = lower_candidate[i] + k = lower_candidate[i] + d/2. Since a solution is guaranteed to exist, we can return thisarr.
This method is too slow for the given constraints but represents a foundational brute-force solution.
Complexity
Time
O(C(2n, n) * n log n) Generating all partitions takes `C(2n, n)` time. For each partition, we perform sorting, which takes `O(n log n)`, and a linear scan, which takes `O(n)`. The sorting step dominates the work done per partition. This complexity is exponential and thus not feasible.
Space
O(n) This space is required to store a single partition (both `lower` and `higher` candidate arrays) during processing.
Trade-offs
Pros
It's a direct, brute-force interpretation of the problem definition.
It is guaranteed to find a valid solution.
Cons
Extremely inefficient due to its exponential time complexity.
The number of partitions grows astronomically with
n, making it infeasible for the given constraints (e.g., forn=15,2n=30,C(30, 15)is over 155 million).
This approach significantly improves upon the naive brute force by using a key insight: after sorting the nums array, the smallest element nums[0] must belong to the lower array. This reduces the search space for k dramatically. We can iterate through possible partners for nums[0], which gives us a candidate for k, and then efficiently verify this k for the entire array in linear time.
Algorithm
- Sort the input array
numsin non-decreasing order. - The smallest element,
nums[0], must be alowerelement. Its correspondinghigherelement isnums[0] + 2k. - Iterate with an index
jfrom1to2n-1. Eachnums[j]is a candidate for being the partner ofnums[0]. - For each
j, calculate the potential differencediff = nums[j] - nums[0]. - If
diffis zero, negative, or odd, it cannot represent2k(sincekmust be a positive integer). Continue to the nextj. - If
diffis valid, we have a candidatek = diff / 2. We must now verify if thiskcan formnpairs(x, x + 2k)from the numbers innums. - To verify
k, use a frequency map (e.g., aHashMap) to store the counts of each number innums. - Iterate through the sorted
numsarray. For eachnum: a. If its count in the map is zero, it has already been used as a partner, so skip it. b. Otherwise, treatnumas alowerelement. Decrement its count in the map. c. Find its required partnerp = num + 2k. d. Check ifpexists in the map with a positive count. If yes, decrementp's count and add the original valuenum + kto our result array. e. Ifpdoes not exist in the map with a positive count, this candidatekis incorrect. Abort the verification for thiskand try the nextj. - If the verification process completes and we have formed an array of size
n, we have found a valid solution. Return it.
Walkthrough
The algorithm begins by sorting the nums array. This is a critical step because it establishes that nums[0] must be a lower value, say arr[i] - k. If it were a higher value, arr[j] + k, then its corresponding lower value, arr[j] - k, would be smaller, which contradicts nums[0] being the minimum element in the array.
The partner for nums[0] is nums[0] + 2k. This partner must be one of the other elements in the array, say nums[j] for some j > 0. We can iterate through j from 1 to 2n-1 to test each nums[j] as a potential partner.
For each j, we calculate a potential difference diff = nums[j] - nums[0]. This diff would be equal to 2k. For k to be a positive integer, diff must be positive and even. If not, we discard this j and move to the next.
If diff is valid, we have a candidate k = diff / 2. We must now verify if this k can explain the entire nums array. This verification can be done efficiently in O(n) time. We use a frequency map (like a HashMap) to store the counts of each number in nums. We then iterate through the sorted nums array. For each number num, if its count is positive, we treat it as a lower value. We find its potential higher partner num + 2k. If this partner also exists in the map with a positive count, we've found a valid pair. We decrement the counts of both num and its partner in the map and add the original value num + k to our result array.
If at any point we cannot find a partner for a number, the candidate k is incorrect. We stop and try the next j. Since the problem guarantees a solution exists, this process will find one.
import java.util.Arrays;import java.util.Map;import java.util.HashMap;import java.util.ArrayList;import java.util.List; class Solution { public int[] recoverArray(int[] nums) { int n = nums.length / 2; Arrays.sort(nums); for (int j = 1; j < nums.length; j++) { int diff = nums[j] - nums[0]; if (diff <= 0 || diff % 2 != 0) { continue; } int k = diff / 2; int[] result = check(nums, k, n); if (result.length == n) { return result; } } return new int[0]; // Should not be reached given the problem constraints } private int[] check(int[] nums, int k, int n) { List<Integer> resList = new ArrayList<>(); Map<Integer, Integer> freq = new HashMap<>(); for (int num : nums) { freq.put(num, freq.getOrDefault(num, 0) + 1); } for (int i = 0; i < nums.length; i++) { int num = nums[i]; if (freq.get(num) == 0) { continue; } freq.put(num, freq.get(num) - 1); int partner = num + 2 * k; if (freq.getOrDefault(partner, 0) > 0) { freq.put(partner, freq.get(partner) - 1); resList.add(num + k); } else { return new int[0]; // Invalid k, this partition is not possible } } // This check is technically redundant if the above loop completes successfully // for a valid k, as resList will always have n elements. if (resList.size() == n) { int[] resultArr = new int[n]; for (int i = 0; i < n; i++) { resultArr[i] = resList.get(i); } return resultArr; } return new int[0]; }}Complexity
Time
O(n^2) Sorting the `nums` array takes `O(n log n)`. The main part of the algorithm is the outer loop that iterates up to `2n-1` times (`O(n)`) to select a candidate `k`. Inside this loop, the `check` function is called. This function builds a frequency map (`O(n)`) and then iterates through `nums` again (`O(n)`), performing constant-time hash map operations on average. So, the verification step is `O(n)`. The total complexity is `O(n log n) + O(n) * O(n) = O(n^2)`.
Space
O(n) The frequency map used for verification can store up to `2n` distinct numbers in the worst case. The result list also requires `O(n)` space. Thus, the space complexity is `O(n)`.
Trade-offs
Pros
Vastly more efficient than the naive exponential approach.
Correct and guaranteed to find the solution under the problem's constraints.
The logic is straightforward and relatively simple to implement.
Cons
The
O(n^2)time complexity might be a concern for significantly larger constraints, although it is efficient enough for the given problem constraints (n <= 1000).
Solutions
Solution
class Solution {public int[] recoverArray(int[] nums) { Arrays.sort(nums); for (int i = 1, n = nums.length; i < n; ++i) { int d = nums[i] - nums[0]; if (d == 0 || d % 2 == 1) { continue; } boolean[] vis = new boolean[n]; vis[i] = true; List<Integer> t = new ArrayList<>(); t.add((nums[0] + nums[i]) >> 1); for (int l = 1, r = i + 1; r < n; ++l, ++r) { while (l < n && vis[l]) { ++l; } while (r < n && nums[r] - nums[l] < d) { ++r; } if (r == n || nums[r] - nums[l] > d) { break; } vis[r] = true; t.add((nums[l] + nums[r]) >> 1); } if (t.size() == (n >> 1)) { int[] ans = new int[t.size()]; int idx = 0; for (int e : t) { ans[idx++] = e; } return ans; } } return null; }}class Solution {public: vector<int> recoverArray(vector<int> &nums) { sort(nums.begin(), nums.end()); for (int i = 1, n = nums.size(); i < n; ++i) { int d = nums[i] - nums[0]; if (d == 0 || d % 2 == 1) continue; vector<bool> vis(n); vis[i] = true; vector<int> ans; ans.push_back((nums[0] + nums[i]) >> 1); for (int l = 1, r = i + 1; r < n; ++l, ++r) { while (l < n && vis[l]) ++l; while (r < n && nums[r] - nums[l] < d) ++r; if (r == n || nums[r] - nums[l] > d) break; vis[r] = true; ans.push_back((nums[l] + nums[r]) >> 1); } if (ans.size() == (n >> 1)) return ans; } return {}; }};class Solution: def recoverArray(self, nums: List[int]) -> List[int]: nums . sort() n = len(nums) for i in range(1, n): d = nums[i] - nums[0] if d == 0 or d % 2 == 1: continue vis = [False] * n vis[i] = True ans = [(nums[0] + nums[i]) >> 1] l, r = 1, i + 1 while r < n: while l < n and vis[l]: l += 1 while r < n and nums[r] - nums[l] < d: r += 1 if r == n or nums[r] - nums[l] > d: break vis[r] = True ans . append((nums[l] + nums[r]) >> 1) l, r = l + 1, r + 1 if len(ans) == (n >> 1): return ans return []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.