2568. Minimum Impossible OR
Problem Description
You are given a 0-indexed integer array nums
.
An integer x
is considered expressible from nums
if you can select some elements from the array (in their original order) and combine them using the bitwise OR operation to get x
. More formally, x
is expressible if there exist indices 0 <= index₁ < index₂ < ... < indexₖ < nums.length
such that nums[index₁] | nums[index₂] | ... | nums[indexₖ] = x
.
Your task is to find the smallest positive integer that cannot be expressed using any subsequence of nums
with the bitwise OR operation.
For example, if nums = [1, 2]
:
1
is expressible (it's in the array)2
is expressible (it's in the array)3
is expressible (1 | 2 = 3
)4
is not expressible (cannot be formed by any OR combination)
The solution leverages the key insight that for any power of 2 (like 1
, 2
, 4
, 8
, etc.) to be expressible, it must exist directly in the array. This is because a power of 2 has only one bit set, and the OR operation can only turn bits on, never off. Therefore, the algorithm checks each power of 2 in increasing order (1
, 2
, 4
, 8
, ...) and returns the first one that doesn't appear in nums
.
The code uses 1 << i
to generate powers of 2 (which is equivalent to 2^i
) and checks if each one exists in a set created from nums
for O(1) lookup time.
Intuition
To understand why we only need to check powers of 2, let's think about how the bitwise OR operation works. The OR operation can only turn bits from 0 to 1, never from 1 to 0. This means we can only "add" bits to our result, never remove them.
Consider any power of 2, like 4
(which is 100
in binary). For 4
to be expressible through OR operations, we need at least one number in our subsequence that has the bit at position 2 set to 1. But here's the crucial insight: if we OR multiple numbers together and none of them have that specific bit set, we can never create it. Since 4
has only that single bit set and all other bits are 0, the only way to express 4
is if 4
itself appears in the array.
Now, what about non-powers of 2? Take 3
(which is 11
in binary). We can express 3
if we can express both 1
(01
) and 2
(10
), because 1 | 2 = 3
. More generally, any integer can be broken down into a sum of distinct powers of 2 (its binary representation), and we can express it if we can express all those constituent powers of 2.
This leads us to the key realization: if all powers of 2 up to a certain point are expressible (exist in the array), then all positive integers up to their sum are also expressible. For instance:
- If we have
1
, we can express1
- If we have
1
and2
, we can express1, 2, 3
- If we have
1
,2
, and4
, we can express all integers from1
to7
Therefore, the smallest unexpressible positive integer must be the smallest power of 2 that doesn't appear in the array. We check 1
, then 2
, then 4
, then 8
, and so on, returning the first one we don't find.
Solution Approach
The implementation follows directly from our understanding that we need to find the smallest power of 2 that doesn't exist in the array.
Step 1: Convert array to set
s = set(nums)
We first convert the array nums
into a set s
for O(1) lookup time. This optimization is important because we'll be checking membership multiple times.
Step 2: Check powers of 2 in ascending order
return next(1 << i for i in range(32) if 1 << i not in s)
This line uses a generator expression with Python's next()
function to find the first power of 2 that isn't in our set:
-
i in range(32)
: We iterate through powers from 0 to 31. Since we're dealing with 32-bit integers (typical constraint in LeetCode problems), checking up to2^31
is sufficient. -
1 << i
: This is a bit shift operation that efficiently calculates2^i
. For example:1 << 0 = 1
(binary:1
)1 << 1 = 2
(binary:10
)1 << 2 = 4
(binary:100
)1 << 3 = 8
(binary:1000
)
-
if 1 << i not in s
: This condition filters to only include powers of 2 that are NOT in our set. -
next()
: Returns the first value from the generator that satisfies our condition.
Why this works:
As established in our intuition, if we have all powers of 2 from 1
to 2^(k-1)
, we can express all integers from 1
to 2^k - 1
through OR operations. The moment we find a missing power of 2, that's our answer - it cannot be expressed through any OR combination of the available numbers.
Time Complexity: O(n) where n is the length of the input array (for creating the set) Space Complexity: O(n) for storing the set
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [1, 2, 6, 8]
:
Step 1: Convert to set for fast lookup
- Create set:
s = {1, 2, 6, 8}
Step 2: Check powers of 2 in ascending order
Check 1 << 0 = 1
:
- Is
1
in set? Yes ✓ (continue)
Check 1 << 1 = 2
:
- Is
2
in set? Yes ✓ (continue)
Check 1 << 2 = 4
:
- Is
4
in set? No ✗ (found our answer!)
Result: 4
Why is 4 the smallest unexpressible integer?
- We can express
1
(directly in array) - We can express
2
(directly in array) - We can express
3
(1 | 2 = 3) - We cannot express
4
because:4
in binary is100
(only bit 2 is set)- Our numbers in binary are:
1 = 001
2 = 010
6 = 110
8 = 1000
- No combination of OR operations can create
100
since none of our numbers have ONLY bit 2 set - Any OR combination that includes
6
would give us110
or higher (extra bits) - The number
8
has a different bit position set entirely
Therefore, 4
is the smallest positive integer that cannot be expressed.
Solution Implementation
1class Solution:
2 def minImpossibleOR(self, nums: List[int]) -> int:
3 # Create a set from the input array for O(1) lookup
4 nums_set = set(nums)
5
6 # Iterate through powers of 2 (1, 2, 4, 8, 16, ...)
7 # Find the smallest power of 2 that is not in the set
8 for i in range(32):
9 power_of_two = 1 << i # Calculate 2^i using bit shift
10 if power_of_two not in nums_set:
11 return power_of_two
12
1class Solution {
2 /**
3 * Finds the minimum impossible OR value that cannot be obtained
4 * by performing bitwise OR on any subset of the given array.
5 *
6 * The key insight is that the minimum impossible OR must be a power of 2.
7 * This is because any number can be represented as a sum of powers of 2,
8 * and OR operation on a subset gives us the sum of distinct powers of 2.
9 *
10 * @param nums the input array of integers
11 * @return the minimum positive integer that cannot be obtained by OR operation
12 */
13 public int minImpossibleOR(int[] nums) {
14 // Create a HashSet to store all unique elements for O(1) lookup
15 Set<Integer> numSet = new HashSet<>();
16
17 // Add all array elements to the set
18 for (int num : nums) {
19 numSet.add(num);
20 }
21
22 // Check each power of 2 starting from 2^0 = 1
23 for (int powerExponent = 0; ; powerExponent++) {
24 int currentPowerOfTwo = 1 << powerExponent; // Calculate 2^powerExponent
25
26 // If this power of 2 is not present in the array,
27 // it's the minimum impossible OR value
28 if (!numSet.contains(currentPowerOfTwo)) {
29 return currentPowerOfTwo;
30 }
31 }
32 }
33}
34
1class Solution {
2public:
3 int minImpossibleOR(vector<int>& nums) {
4 // Create a hash set for O(1) lookup of elements in nums
5 unordered_set<int> numSet(nums.begin(), nums.end());
6
7 // Check powers of 2 starting from 2^0 = 1
8 // The minimum impossible OR must be a power of 2
9 // because if we can create all powers of 2 up to 2^k,
10 // we can create any number with bits set only in positions 0 to k
11 for (int power = 0; ; ++power) {
12 int powerOfTwo = 1 << power; // Calculate 2^power
13
14 // If this power of 2 is not in the array,
15 // it cannot be created by OR operations
16 if (!numSet.count(powerOfTwo)) {
17 return powerOfTwo;
18 }
19 }
20 }
21};
22
1/**
2 * Finds the minimum impossible OR value that cannot be obtained
3 * by performing bitwise OR on any subset of the given array.
4 *
5 * The algorithm checks for the smallest power of 2 that is not present
6 * in the array, as this will be the minimum impossible OR value.
7 *
8 * @param nums - Array of positive integers
9 * @returns The minimum impossible OR value
10 */
11function minImpossibleOR(nums: number[]): number {
12 // Create a set to store unique values for O(1) lookup
13 const uniqueNumbers: Set<number> = new Set<number>();
14
15 // Add all numbers from the input array to the set
16 for (const num of nums) {
17 uniqueNumbers.add(num);
18 }
19
20 // Check powers of 2 starting from 2^0 = 1
21 for (let exponent = 0; ; exponent++) {
22 const powerOfTwo = 1 << exponent; // Calculate 2^exponent using bit shift
23
24 // If this power of 2 is not in the set, it's the minimum impossible OR
25 if (!uniqueNumbers.has(powerOfTwo)) {
26 return powerOfTwo;
27 }
28 }
29}
30
Time and Space Complexity
The time complexity is O(n + log M)
, where n
is the length of the array nums
and M
is the maximum value in the array nums
.
- Creating the set
s
fromnums
takesO(n)
time. - The generator expression iterates through powers of 2 (i.e.,
1 << i
fori
from 0 to 31). In the worst case, it checks up to 32 values, but since we're looking for the minimum impossible OR value, it will stop at the first power of 2 not in the set. The number of powers of 2 to check is bounded byO(log M)
whereM
is the maximum value innums
, since any power of 2 greater thanM
cannot be formed by OR operations on elements ≤M
. - Each membership check (
1 << i not in s
) takesO(1)
average time for a set. - Therefore, the total time complexity is
O(n) + O(log M) × O(1) = O(n + log M)
.
The space complexity is O(n)
because we create a set s
that stores all unique elements from the input array nums
, which in the worst case contains n
distinct elements.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Attempting to Generate All Possible OR Combinations
The Wrong Approach: Many people's first instinct is to generate all possible OR combinations from the array and then find the smallest missing positive integer:
def minImpossibleOR(self, nums: List[int]) -> int:
# WRONG: This tries to generate all possible OR values
possible_values = set()
n = len(nums)
# Generate all subsequences and their OR values
for mask in range(1, 1 << n): # All non-empty subsequences
or_value = 0
for i in range(n):
if mask & (1 << i):
or_value |= nums[i]
possible_values.add(or_value)
# Find smallest missing positive integer
result = 1
while result in possible_values:
result += 1
return result
Why This Fails:
- Time complexity is O(2^n × n), which causes Time Limit Exceeded for large inputs
- The approach doesn't leverage the key insight about powers of 2
- It's unnecessarily complex for what the problem actually requires
Pitfall 2: Checking All Positive Integers Sequentially
The Wrong Approach:
def minImpossibleOR(self, nums: List[int]) -> int:
# WRONG: Checking if each integer can be formed
nums_set = set(nums)
for candidate in range(1, max(nums) + 2):
# Try to check if candidate can be formed by OR operations
# This gets complicated and inefficient quickly
can_form = False
# ... complex logic to check if candidate is expressible ...
if not can_form:
return candidate
Why This Fails:
- Checking whether an arbitrary number can be formed through OR operations is complex
- The approach misses the fundamental property that powers of 2 are the limiting factor
- Could lead to incorrect results if the checking logic isn't perfectly implemented
Pitfall 3: Forgetting Edge Cases with Large Numbers
The Wrong Approach:
def minImpossibleOR(self, nums: List[int]) -> int:
nums_set = set(nums)
# WRONG: Only checking small range
for i in range(20): # Arbitrary small limit
if (1 << i) not in nums_set:
return 1 << i
Why This Fails:
- If the array contains all powers of 2 up to a large value, this could miss the answer
- Should check up to at least 30-31 bits for typical integer constraints
The Correct Solution Addresses These Pitfalls:
class Solution:
def minImpossibleOR(self, nums: List[int]) -> int:
nums_set = set(nums)
# Check powers of 2 up to reasonable limit (32 bits)
for i in range(32):
power_of_two = 1 << i
if power_of_two not in nums_set:
return power_of_two
Key Insights to Remember:
- Only powers of 2 matter for determining what cannot be expressed
- Use set for O(1) lookups instead of repeated array searches
- Check sufficient range (32 bits covers all standard integer values)
- Keep the solution simple - don't overcomplicate with subsequence generation
What does the following code do?
1def f(arr1, arr2):
2 i, j = 0, 0
3 new_arr = []
4 while i < len(arr1) and j < len(arr2):
5 if arr1[i] < arr2[j]:
6 new_arr.append(arr1[i])
7 i += 1
8 else:
9 new_arr.append(arr2[j])
10 j += 1
11 new_arr.extend(arr1[i:])
12 new_arr.extend(arr2[j:])
13 return new_arr
14
1public static List<Integer> f(int[] arr1, int[] arr2) {
2 int i = 0, j = 0;
3 List<Integer> newArr = new ArrayList<>();
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.add(arr1[i]);
8 i++;
9 } else {
10 newArr.add(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.add(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.add(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
1function f(arr1, arr2) {
2 let i = 0, j = 0;
3 let newArr = [];
4
5 while (i < arr1.length && j < arr2.length) {
6 if (arr1[i] < arr2[j]) {
7 newArr.push(arr1[i]);
8 i++;
9 } else {
10 newArr.push(arr2[j]);
11 j++;
12 }
13 }
14
15 while (i < arr1.length) {
16 newArr.push(arr1[i]);
17 i++;
18 }
19
20 while (j < arr2.length) {
21 newArr.push(arr2[j]);
22 j++;
23 }
24
25 return newArr;
26}
27
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!