Initialize Trie and Prepare to Insert First Number (3)
Create an empty trie and prepare to insert the first number, 3, processing its bits from the 31st (MSB) to the 0th (LSB).
💡 Initializing the trie is essential to store all numbers bit-by-bit for efficient XOR queries.
Line:trie = Trie()
num = 3
trie.insert(num)
💡 The trie starts empty and will be built by inserting each number's bits from MSB to LSB.
insert
Insert bit 31 of number 3 into trie
Extract the 31st bit of 3 (which is 0) and insert it into the trie. Since this path does not exist, create a new node for bit 0.
💡 Inserting bits one by one builds the trie structure representing all numbers in binary form.
Line:bit = (num >> 31) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 The trie grows dynamically as new bit paths are inserted for each number.
insert
Insert bit 30 of number 3 into trie
Move to the 30th bit of 3 (also 0) and insert it into the trie under the previously created node for bit 0.
💡 Continuing insertion bit-by-bit ensures the trie fully represents the number's binary form.
Line:bit = (num >> 30) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 Each bit extends the trie path deeper, representing the number's bits.
insert
Insert bit 1 of number 3 into trie
Insert the 1st bit of 3 (which is 1) into the trie, creating a new branch distinct from previous zeros.
💡 Bits with value 1 create branches distinct from 0 bits, differentiating numbers in the trie.
Line:bit = (num >> 1) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 The trie branches at bits where numbers differ, enabling XOR maximization later.
insert
Insert bit 0 of number 3 into trie
Insert the 0th bit of 3 (which is 1), completing the insertion of the number's bits into the trie.
💡 Completing insertion ensures the trie fully represents the number for future XOR queries.
Line:bit = (num >> 0) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 The trie now contains the full bit path for number 3.
insert
Insert next number 10 into trie starting at bit 31
Begin inserting the next number, 10, starting from the 31st bit. Check if bit 0 exists in trie and move down existing path if so.
💡 Numbers with common prefixes share trie nodes, saving space and enabling efficient queries.
Line:num = 10
bit = (num >> 31) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 Common leading zeros share trie nodes.
insert
Insert bit 3 of number 10 into trie
Insert the 3rd bit of 10 (which is 1). Since this bit path does not exist, create a new node for bit 1.
💡 New bits create new branches in the trie, differentiating numbers.
Line:bit = (num >> 3) & 1
if bit not in node.children:
node.children[bit] = TrieNode()
💡 Trie branches represent bit differences between numbers.
traverse
Start traversal to find max XOR for number 5
Begin traversing the trie to find the number that maximizes XOR with 5. At bit 31, bit is 0, toggled bit is 1, but no child 1 exists, so move down bit 0 path.
💡 Choosing opposite bits at each step maximizes XOR bitwise.
Line:node = trie.root
max_xor = 0
for i in range(31, -1, -1):
bit = (num >> i) & 1
toggled_bit = 1 - bit
if toggled_bit in node.children:
max_xor |= (1 << i)
node = node.children[toggled_bit]
else:
node = node.children.get(bit)
💡 If opposite bit path exists, XOR bit is set to 1; else follow same bit path.
traverse
Traverse bit 4 for number 5 to maximize XOR
At bit 4, bit of 5 is 0, toggled bit is 1, which exists in trie. Set bit 4 in max_xor and move down that path.
💡 Choosing the opposite bit path sets the XOR bit to 1 at that position, increasing XOR value.
Line:bit = (num >> 4) & 1
if toggled_bit in node.children:
max_xor |= (1 << 4)
node = node.children[toggled_bit]
💡 This step contributes a 1 in the XOR result at bit 4.
traverse
Traverse bit 3 for number 5 to maximize XOR
At bit 3, bit of 5 is 0, toggled bit is 1, which exists in trie. Set bit 3 in max_xor and move down that path.
💡 Continuing to pick opposite bits maximizes XOR bitwise.
Line:bit = (num >> 3) & 1
if toggled_bit in node.children:
max_xor |= (1 << 3)
node = node.children[toggled_bit]
💡 XOR bit 3 is set to 1, increasing max_xor by 8.
traverse
Traverse bit 0 for number 5 to maximize XOR
At bit 0, bit of 5 is 1, toggled bit is 0. Since bit 0 exists in trie children, set bit 0 in max_xor and move down that path.
💡 If opposite bit exists, set XOR bit; else follow same bit path.
After traversal, the maximum XOR found for number 5 is 25. Update max_xor from 0 to 25.
💡 Tracking the maximum XOR found so far is essential to find the final answer.
Line:max_xor = max(max_xor, trie.findMaxXor(num))
💡 The maximum XOR so far is 25 after processing number 5.
traverse
Traverse number 25 to find max XOR
Traverse the trie for number 25. At bit 4, bit is 1, toggled bit is 0, which exists, so set bit 4 in max_xor.
💡 Traversal tries to pick opposite bits to maximize XOR at each bit position.
Line:bit = (num >> 4) & 1
if toggled_bit in node.children:
max_xor |= (1 << 4)
node = node.children[toggled_bit]
💡 Setting bit 4 in max_xor contributes 16 to XOR value.
traverse
Traverse bit 3 for number 25 to maximize XOR
At bit 3, bit is 1, toggled bit is 0, which exists in trie. Set bit 3 in max_xor and move down that path.
💡 Continuing to pick opposite bits maximizes XOR.
Line:bit = (num >> 3) & 1
if toggled_bit in node.children:
max_xor |= (1 << 3)
node = node.children[toggled_bit]
💡 XOR bit 3 set to 1, adding 8 to max_xor.
traverse
Traverse bit 0 for number 25 to maximize XOR
At bit 0, bit is 1, toggled bit is 0, which exists in trie. Set bit 0 in max_xor and move down that path.
💡 Final bits can still contribute to XOR maximization.
Line:bit = (num >> 0) & 1
if toggled_bit in node.children:
max_xor |= (1 << 0)
node = node.children[toggled_bit]
💡 XOR bit 0 set to 1, adding 1 to max_xor.
compare
Update max XOR after checking number 25
The maximum XOR found for number 25 is 25, which matches the current max_xor, so no update is needed.
💡 We keep track of the highest XOR found so far.
Line:max_xor = max(max_xor, trie.findMaxXor(num))
💡 Current max_xor is still 25 after processing 25.
traverse
Traverse number 5 again to find max XOR
Traverse the trie for number 5 again to check for a better XOR. At bit 4, toggled bit 1 exists, setting bit 4 in max_xor.
💡 Repeated traversals check all numbers for maximum XOR pairs.
Line:max_xor = max(max_xor, trie.findMaxXor(num))
💡 A new maximum XOR of 28 is found with pair (5, 25).
reconstruct
Final max XOR is 28
After processing all numbers, the maximum XOR found is 28, which is the XOR of 5 and 25.
💡 The algorithm returns the maximum XOR found after all traversals.
Line:return max_xor
💡 The maximum XOR pair is identified by the highest XOR value found.
class TrieNode:
def __init__(self):
self.children = {}
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, num):
node = self.root
for i in range(31, -1, -1): # STEP: Insert bits from MSB to LSB
bit = (num >> i) & 1
if bit not in node.children:
node.children[bit] = TrieNode() # STEP: Create new node if needed
node = node.children[bit]
def findMaxXor(self, num):
node = self.root
max_xor = 0
for i in range(31, -1, -1): # STEP: Traverse bits from MSB to LSB
bit = (num >> i) & 1
toggled_bit = 1 - bit
if toggled_bit in node.children:
max_xor |= (1 << i) # STEP: Set bit in max_xor
node = node.children[toggled_bit]
else:
node = node.children.get(bit)
return max_xor
def findMaximumXOR(nums):
trie = Trie() # STEP: Initialize trie
for num in nums:
trie.insert(num) # STEP: Insert each number
max_xor = 0
for num in nums:
max_xor = max(max_xor, trie.findMaxXor(num)) # STEP: Find max XOR for each number
return max_xor
if __name__ == '__main__':
nums = [3, 10, 5, 25, 2, 8]
print(findMaximumXOR(nums)) # Output: 28
📊
Maximum XOR of Two Numbers - Watch the Algorithm Execute, Step by Step
Watching the bitwise trie construction and traversal visually reveals how XOR maximization works at the bit level, which is hard to grasp from code alone.
Step 1/18
·Active fill★Answer cell
Check MSB of 3
A
0
1
1
=3
B
💡 Starting insertion of number 3 from MSB to LSB
Insert bit 0 at position 31
A
0
1
1
=3
B
💡 Created new trie node for bit 0 at MSB
Insert bit 0 at position 30
A
0
1
1
=3
B
💡 Extended trie path with bit 0 at position 30
Insert bit 1 at position 1
A
0
1
1
=3
B
💡 Created branch for bit 1 at position 1
Insert bit 1 at position 0
A
0
1
1
=3
B
💡 Completed insertion of number 3
Check MSB of 10
A
0
1
0
1
0
=10
B
💡 Traverse existing trie node for bit 0 at MSB
Insert bit 1 at position 3
A
0
1
0
1
0
=10
B
💡 Created new trie node for bit 1 at bit 3
Check bit 31 of 5 and trie
A
0
1
0
1
=5
B=000000000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=0
💡 No opposite bit 1 at MSB, follow bit 0 path
Set bit 4 in max_xor
A
0
1
0
1
=5
B=000010000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=16
💡 Opposite bit found, XOR bit set to 1 at position 4
Set bit 3 in max_xor
A
0
1
0
1
=5
B=000110000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=24
💡 Opposite bit found, XOR bit set to 1 at position 3
Set bit 0 in max_xor
A
0
1
0
1
=5
B=000110000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
1
=25
💡 Opposite bit found, XOR bit set to 1 at position 0
Update max_xor to 25
A
0
1
0
1
=5
B
💡 max_xor updated to 25 after traversal
Set bit 4 in max_xor
A
0
1
1
0
0
1
=25
B=000000000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=16
💡 Opposite bit found, XOR bit set to 1 at position 4
Set bit 3 in max_xor
A
0
1
1
0
0
1
=25
B=000000000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=24
💡 Opposite bit found, XOR bit set to 1 at position 3
Set bit 0 in max_xor
A
0
1
1
0
0
1
=25
B=000000000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
1
=25
💡 Opposite bit found, XOR bit set to 1 at position 0
max_xor remains 25
A
0
1
1
0
0
1
=25
B
💡 max_xor unchanged after traversal
Set bits 4 and 3 in max_xor
A
0
1
0
1
=5
B=000010000000000000000000000000000
────────────────
=
0
0
0
0
0
0
0
0
=28
💡 New max_xor 28 found by choosing opposite bits
Return max_xor = 28
A
0
1
1
1
0
0
=28
B
────────────
=
0
1
1
1
0
0
=28
💡 Maximum XOR of two numbers in input is 28
Key Takeaways
✓ Building a trie of bits from MSB to LSB allows efficient representation of all numbers for XOR queries.
This insight is hard to see from code alone because the trie structure and bitwise operations are abstract without visualization.
✓ During traversal, choosing the opposite bit path whenever possible maximizes the XOR bitwise.
Visualizing this choice at each bit clarifies why XOR is maximized by picking opposite bits.
✓ The maximum XOR is found by comparing XOR values obtained from traversing the trie for each number.
Seeing the max_xor update step-by-step shows how the algorithm finds the global maximum.
Practice
(1/5)
1. Consider the following code that generates all permutations of 3 elements using bitmask DP with iterative tabulation. What is the content of dp[7] after the loops finish?
def generate_permutations_iterative(n):
dp = [[] for _ in range(1 << n)]
dp[0] = [[]]
for mask in range(1 << n):
for i in range(n):
if not (mask & (1 << i)):
for perm in dp[mask]:
dp[mask | (1 << i)].append(perm + [i])
return dp[(1 << n) - 1]
perms = generate_permutations_iterative(3)
print(perms)
easy
A. [[0,1,2], [0,2,1], [1,0,2], [1,2,0], [2,0,1], [2,1,0]]
B. [[0,1,2], [1,0,2], [2,0,1]]
C. [[0,1,2], [0,2,1], [1,0,2], [2,1,0]]
D. [[0,1,2], [1,2,0], [2,1,0], [2,0,1]]
Solution
Step 1: Trace dp[0] initialization
dp[0] starts with [[]], representing empty permutation.
Step 2: Iteratively build dp for all masks
For each mask, append unused elements to existing permutations, eventually filling dp[7] with all permutations of [0,1,2].
Final Answer:
Option A -> Option A
Quick Check:
dp[7] contains all 3! = 6 permutations of [0,1,2] [OK]
Hint: dp[(1<
Common Mistakes:
Confusing mask indexing leading to missing permutations
Off-by-one errors in bitmask checks
2. Identify the bug in the following code that attempts to check if a number is a power of two:
def isPowerOfTwo(n: int) -> bool:
while n % 2 == 0:
n //= 2
return n == 1
What is the issue?
medium
A. The return statement should check n == 0 instead of n == 1
B. The loop condition should be n > 1 instead of n % 2 == 0
C. Using integer division //= 2 causes infinite loops for odd numbers
D. The code does not handle zero or negative inputs, leading to incorrect results
Solution
Step 1: Analyze input handling
The code does not check if n is positive before the loop, so zero or negative inputs cause incorrect behavior.
Step 2: Consequence of missing check
For n=0 or negative, the loop condition n % 2 == 0 may cause infinite loop or wrong return value.
Final Answer:
Option D -> Option D
Quick Check:
Proper input validation is required to avoid incorrect results [OK]
Hint: Always check n > 0 before division loops [OK]
Common Mistakes:
Forgetting zero/negative input checks
Confusing loop condition with termination condition
3. What is the time complexity of the backtracking approach to generate all subsets of an input array of size n, assuming the array is sorted beforehand?
medium
A. O(n * 2^n)
B. O(n^2)
C. O(2^n)
D. O(n!)
Solution
Step 1: Identify number of subsets generated
There are 2^n subsets in total.
Step 2: Calculate work done per subset
Each subset can take up to O(n) time to copy or build, so total time is O(n * 2^n).
Final Answer:
Option A -> Option A
Quick Check:
Exponential subsets times linear work per subset [OK]
Hint: Each subset generation costs O(n), total subsets 2^n [OK]
Common Mistakes:
Confusing O(2^n) as total time ignoring subset size
Mistaking factorial or polynomial complexities
4. If the input array can contain negative numbers and the bit counting approach is used, what modification is necessary to correctly find the single number?
hard
A. Use a 32-bit mask and handle sign bit separately to avoid sign extension errors.
B. Use a hash map instead, as bit counting cannot handle negatives.
C. Convert all numbers to positive before bit counting and restore sign later.
D. No modification needed; bit counting works the same for negatives.
Solution
Step 1: Understand bit counting with negative numbers
Negative numbers use two's complement; sign bit must be handled carefully to avoid incorrect counts.
Step 2: Apply 32-bit mask and handle sign bit
Counting bits with a mask ensures correct accumulation; sign bit handled separately prevents sign extension issues.
Final Answer:
Option A -> Option A
Quick Check:
Proper masking ensures correct bit counts for negatives [OK]
Hint: Mask bits to handle sign bit correctly in negative numbers [OK]
Common Mistakes:
Ignoring sign bit causes wrong results
Assuming negatives behave like positives
5. Suppose you want to extend the sum of two integers without using + operator to support arbitrary large integers beyond 32 bits. Which modification is necessary to ensure correctness?
hard
A. Use built-in + operator for large integers since bitwise approach only works for fixed 32-bit integers.
B. Keep masking with 0xFFFFFFFF but increase mask size dynamically based on input bit length.
C. Implement iterative approach without recursion to avoid stack overflow on large inputs.
D. Remove masking with 0xFFFFFFFF to allow Python's unlimited integer size to handle sums.
Solution
Step 1: Identify recursion depth issue
For very large integers, recursion depth grows with bit length, risking stack overflow.
Step 2: Use iterative approach to handle arbitrary large integers
Iterative bitwise sum avoids recursion stack limits and handles large inputs safely.
Step 3: Masking must adapt or be removed carefully
Masking fixed to 32 bits breaks large integer sums; iterative approach can handle masking dynamically.
Final Answer:
Option C -> Option C
Quick Check:
Iterative avoids recursion limits for large inputs [OK]
Hint: Recursion depth limits large input sums; iteration needed [OK]
Common Mistakes:
Removing mask blindly breaks sign handling
Using + operator defeats problem purpose
Static mask size limits input range
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.