Check If N and Its Double Exist
EasyPrompt
Given an array arr of integers, check if there exist two indices i and j such that :
i != j0 <= i, j < arr.lengtharr[i] == 2 * arr[j]
Example 1:
Input: arr = [10,2,5,3]
Output: true
Explanation: For i = 0 and j = 2, arr[i] == 10 == 2 * 5 == 2 * arr[j]Example 2:
Input: arr = [3,1,7,11]
Output: false
Explanation: There is no i and j that satisfy the conditions.
Constraints:
2 <= arr.length <= 500-103 <= arr[i] <= 103
Approaches
3 approaches with complexity analysis and trade-offs.
The most straightforward approach is to check every possible pair of elements in the array. We can use two nested loops to iterate through all pairs of indices (i, j) and verify if the condition arr[i] == 2 * arr[j] holds, ensuring that i and j are not the same.
Algorithm
- Initialize two nested loops, with iterators
iandj, both from0toarr.length - 1. - Inside the inner loop, check if the indices are different (
i != j). - If the indices are different, check if the condition
arr[i] == 2 * arr[j]is met. - If the condition is true, a valid pair has been found, so return
trueimmediately. - If the loops complete without finding any such pair, it means no such pair exists. Return
false.
Walkthrough
This method involves a systematic check of all pairs. The outer loop selects an element arr[i], and the inner loop iterates through all other elements arr[j] to see if arr[j] is half of arr[i]. The algorithm is as follows:
class Solution { public boolean checkIfExist(int[] arr) { int n = arr.length; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { // The indices must be different if (i != j && arr[i] == 2 * arr[j]) { return true; } } } return false; }}Complexity
Time
O(N^2), where N is the number of elements in the array. For each element, we iterate through the entire array again, leading to a quadratic number of comparisons.
Space
O(1), as we only use a few variables to store indices and the array length, which does not depend on the input size.
Trade-offs
Pros
Simple to understand and implement.
Requires no extra memory, making it space-efficient.
Cons
Highly inefficient for large arrays due to its quadratic time complexity.
Performs many redundant checks.
A more optimized approach involves sorting the array first. Once the array is sorted, for each element x, we can use binary search to efficiently find if its double (2*x) exists in the array. This avoids the need for a linear scan for each element.
Algorithm
- First, sort the input array
arrin non-decreasing order. - Iterate through the sorted array with an index
ifrom0toarr.length - 1. - For each element
arr[i], calculate thetargetvalue, which is2 * arr[i]. - Perform a binary search for the
targetin the rest of the array, excluding the element at indexi. - If the binary search finds the
target, returntrue. - If the loop finishes without finding any such pair, return
false.
Walkthrough
By sorting the array, we can leverage the ordered property to speed up the search. For each element arr[i], we calculate the target value (2 * arr[i]). Then, we perform a binary search for this target in the array. A crucial detail is to ensure that if the target is found at an index j, it must be a different element, i.e., j != i. This is particularly important for the case where arr[i] is 0. A robust way to handle this is to define the search space for the binary search to exclude the current index i.
import java.util.Arrays; class Solution { public boolean checkIfExist(int[] arr) { Arrays.sort(arr); for (int i = 0; i < arr.length; i++) { int target = 2 * arr[i]; // Define the search range to exclude the current element. // If target is larger, search to the right. If smaller, search to the left. int low, high; if (arr[i] >= 0) { low = i + 1; high = arr.length - 1; } else { // arr[i] < 0, so 2 * arr[i] < arr[i] low = 0; high = i - 1; } // Standard binary search in the defined range while (low <= high) { int mid = low + (high - low) / 2; if (arr[mid] == target) { return true; } else if (arr[mid] < target) { low = mid + 1; } else { high = mid - 1; } } } return false; }}Complexity
Time
O(N log N). Sorting the array takes O(N log N). The subsequent loop runs N times, with each iteration performing a binary search that takes O(log N). The total time is dominated by the sorting step.
Space
O(log N) to O(N), depending on the implementation of the sorting algorithm. In Java, `Arrays.sort()` for primitive types has an average space complexity of O(log N) for the recursion stack.
Trade-offs
Pros
Significantly faster than the brute-force approach for larger arrays.
A good trade-off between time and space complexity.
Cons
The in-place sort modifies the original array, which might not be desirable.
More complex to implement correctly, especially handling the
i != jcondition with binary search.
The most efficient solution uses a HashSet to achieve linear time complexity. We iterate through the array, and for each element, we check if its double or its half already exists in the set of numbers we've seen so far. This avoids repeated computations and searches.
Algorithm
- Initialize an empty
HashSetcalledseento store the numbers encountered so far. - Iterate through each number
numin the input arrayarr. - For each
num, check ifseencontains2 * num. - Also, check if
numis an even number and ifseencontainsnum / 2. - If either of the above checks is true, it means we have found a valid pair. Return
true. - If not, add the current
numto theseenset. - If the loop completes, it means no such pair was found. Return
false.
Walkthrough
We can use a HashSet for O(1) average time complexity lookups. We iterate through the array arr once. For each number num, we check two conditions before adding it to our set of seen numbers:
- Does
2 * numexist in the set? If so, we've found a number that is half of an existing number. - Is
numeven, and doesnum / 2exist in the set? If so, we've found a number that is double an existing number. If either of these conditions is true, we returntrue. Otherwise, we add the current numbernumto the set and continue. This ensures we find a pair regardless of the order they appear in the array.
import java.util.HashSet;import java.util.Set; class Solution { public boolean checkIfExist(int[] arr) { Set<Integer> seen = new HashSet<>(); for (int num : arr) { // Check if the double exists or if the half exists if (seen.contains(2 * num) || (num % 2 == 0 && seen.contains(num / 2))) { return true; } // Add the current number to the set for future checks seen.add(num); } return false; }}Complexity
Time
O(N), where N is the number of elements in the array. We iterate through the array once, and each HashSet operation (insertion and lookup) takes O(1) time on average.
Space
O(N), as in the worst-case scenario (no duplicates and no valid pairs), the HashSet will store all N elements from the array.
Trade-offs
Pros
Optimal time complexity of O(N).
The logic is straightforward and handles all cases, including zeros, elegantly.
Cons
Requires extra space proportional to the number of elements in the array.
Solutions
Solution
class Solution {public boolean checkIfExist(int[] arr) { Map<Integer, Integer> m = new HashMap<>(); int n = arr.length; for (int i = 0; i < n; ++i) { m.put(arr[i], i); } for (int i = 0; i < n; ++i) { if (m.containsKey(arr[i] << 1) && m.get(arr[i] << 1) != i) { return true; } } return false; }}/** * @param {number[]} arr * @return {boolean} */ var checkIfExist = function ( arr,) { const s = new Set(); for (const v of arr) { if (s.has(v << 1) || s.has(v / 2)) { return true; } s.add(v); } return false;};class Solution {public: bool checkIfExist(vector<int> &arr) { unordered_map<int, int> m; int n = arr.size(); for (int i = 0; i < n; ++i) m[arr[i]] = i; for (int i = 0; i < n; ++i) if (m.count(arr[i] * 2) && m[arr[i] * 2] != i) return true; return false; }};class Solution: def checkIfExist(self, arr: List[int]) -> bool: m = {v: i for i, v in enumerate(arr)} return any(v << 1 in m and m[v << 1] != i for i, v in enumerate(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.