Skip to content

Commit dffdd80

Browse files
authored
Merge pull request #1484 from tanyagupta01/maximum-xor
Create 0421-maximum-xor-of-two-numbers-in-an-array.md
2 parents 63bd068 + 998b5be commit dffdd80

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
id: maximum-xor-of-two-numbers-in-an-array
3+
title: Maximum XOR of two numbers in an array
4+
sidebar_label: 0421-maximum-xor-of-two-numbers-in-an-array
5+
tags:
6+
- Array
7+
- Hash Table
8+
- Bit Manipulation
9+
- Trie
10+
description: "This is a solution to the maximum xor of two numbers in an array problem on LeetCode."
11+
---
12+
13+
## Problem Description
14+
15+
Given an integer array `nums`, return the maximum result of `nums[i] XOR nums[j]`, where `0 <= i <= j < n`.
16+
17+
### Examples
18+
19+
**Example 1:**
20+
21+
```
22+
Input: nums = [3,10,5,25,2,8]
23+
Output: 28
24+
Explanation: The maximum result is 5 XOR 25 = 28.
25+
```
26+
27+
**Example 2:**
28+
29+
```
30+
Input: nums = [14,70,53,83,49,91,36,80,92,51,66,70]
31+
Output: 127
32+
```
33+
34+
### Constraints
35+
36+
- `1 <= nums.length <= 2 * 105`
37+
- `0 <= nums[i] <= 231 - 1`
38+
39+
## Solution for Strong Password Checker
40+
41+
### Approach
42+
43+
XOR, or exclusive OR, is a binary bitwise operation that returns true (1) only when the operands differ. If the bits are the same, the result is 0. If the bits are different, the result is 1.
44+
45+
The goal is to maximise the XOR result between two numbers This is achieved by ensuring for each bit position, the XOR result is maximum hence we want to maximise the number of opposite bits between the numbers.
46+
47+
We use a Trie which allows us to iterate through each bit of the numbers in the array and maximise XOR value by selecting the opposite bit when available hence ensuring the highest possible XOR result.
48+
49+
Step 1:Create a Trie Node Structure. This structure represents a node in the Trie.
50+
51+
- It contains an array (‘links’) to store links to child nodes (0 and 1).
52+
- Provides methods to interact with the child nodes like ‘containsKey’, ‘get’ and ‘put’.
53+
54+
Step 2: Iterate through the given array and insert its bit values into the Trie from left to right.
55+
56+
- For each number, check if the current node has a child node with the current bit. If not, create a new child node with the current bit.
57+
- Move to the child node corresponding to the current bit.
58+
59+
Step 3: Start from the root node. Initialise the maximum XOR values as 0.
60+
61+
Step 4: Iterate through each bit of the number (from left to right). Check if the complement of the current bit exists in the Trie. If so:
62+
63+
- Update the maximum XOR value with the current bit.
64+
- Moves to the child node corresponding to the complement of the current bit.
65+
66+
## Code in Different Languages
67+
68+
<Tabs>
69+
<TabItem value="cpp" label="C++">
70+
<SolutionAuthor name="@tanyagupta01"/>
71+
72+
```cpp
73+
class Solution {
74+
public:
75+
int findMaximumXOR(vector<int>& nums) {
76+
const int maxNum = ranges::max(nums);
77+
if (maxNum == 0)
78+
return 0;
79+
const int maxBit = static_cast<int>(log2(maxNum));
80+
int ans = 0;
81+
int prefixMask = 0; // Grows like: 10000 -> 11000 -> ... -> 11111.
82+
83+
// If ans is 11100 when i = 2, it means that before we reach the last two
84+
// bits, 11100 is the maximum XOR we have, and we're going to explore if we
85+
// can get another two 1s and put them into `ans`.
86+
for (int i = maxBit; i >= 0; --i) {
87+
prefixMask |= 1 << i;
88+
unordered_set<int> prefixes;
89+
// We only care about the left parts,
90+
// If i = 2, nums = {1110, 1011, 0111}
91+
// -> prefixes = {1100, 1000, 0100}
92+
for (const int num : nums)
93+
prefixes.insert(num & prefixMask);
94+
// If i = 1 and before this iteration, the ans is 10100, it means that we
95+
// want to grow the ans to 10100 | 1 << 1 = 10110 and we're looking for
96+
// XOR of two prefixes = candidate.
97+
const int candidate = ans | 1 << i;
98+
for (const int prefix : prefixes)
99+
if (prefixes.count(prefix ^ candidate)) {
100+
ans = candidate;
101+
break;
102+
}
103+
}
104+
105+
return ans;
106+
}
107+
};
108+
```
109+
</TabItem>
110+
<TabItem value="java" label="Java">
111+
<SolutionAuthor name="@tanyagupta01"/>
112+
113+
```java
114+
class Solution {
115+
public int findMaximumXOR(int[] nums) {
116+
final int maxNum = Arrays.stream(nums).max().getAsInt();
117+
if (maxNum == 0)
118+
return 0;
119+
final int maxBit = (int) (Math.log(maxNum) / Math.log(2));
120+
int ans = 0;
121+
int prefixMask = 0; // Grows like: 10000 -> 11000 -> ... -> 11111.
122+
123+
// If ans is 11100 when i = 2, it means that before we reach the last two
124+
// bits, 11100 is the maximum XOR we have, and we're going to explore if we
125+
// can get another two 1s and put them into `ans`.
126+
for (int i = maxBit; i >= 0; --i) {
127+
prefixMask |= 1 << i;
128+
Set<Integer> prefixes = new HashSet<>();
129+
// We only care about the left parts,
130+
// If i = 2, nums = {1110, 1011, 0111}
131+
// . prefixes = {1100, 1000, 0100}
132+
for (final int num : nums)
133+
prefixes.add(num & prefixMask);
134+
// If i = 1 and before this iteration, the ans is 10100, it means that we
135+
// want to grow the ans to 10100 | 1 << 1 = 10110 and we're looking for
136+
// XOR of two prefixes = candidate.
137+
final int candidate = ans | 1 << i;
138+
for (final int prefix : prefixes)
139+
if (prefixes.contains(prefix ^ candidate)) {
140+
ans = candidate;
141+
break;
142+
}
143+
}
144+
145+
return ans;
146+
}
147+
}
148+
```
149+
150+
</TabItem>
151+
<TabItem value="python" label="Python">
152+
<SolutionAuthor name="@tanyagupta01"/>
153+
154+
```python
155+
class Solution:
156+
def findMaximumXOR(self, nums: List[int]) -> int:
157+
maxNum = max(nums)
158+
if maxNum == 0:
159+
return 0
160+
maxBit = int(math.log2(maxNum))
161+
ans = 0
162+
prefixMask = 0 # `prefixMask` grows like: 10000 -> 11000 -> ... -> 11111.
163+
164+
# If ans is 11100 when i = 2, it means that before we reach the last two
165+
# bits, 11100 is the maximum XOR we have, and we're going to explore if we
166+
# can get another two 1s and put them into `ans`.
167+
for i in range(maxBit, -1, -1):
168+
prefixMask |= 1 << i
169+
# We only care about the left parts,
170+
# If i = 2, nums = [1110, 1011, 0111]
171+
# -> prefixes = [1100, 1000, 0100]
172+
prefixes = set([num & prefixMask for num in nums])
173+
# If i = 1 and before this iteration, the ans is 10100, it means that we
174+
# want to grow the ans to 10100 | 1 << 1 = 10110 and we're looking for
175+
# XOR of two prefixes = candidate.
176+
candidate = ans | 1 << i
177+
for prefix in prefixes:
178+
if prefix ^ candidate in prefixes:
179+
ans = candidate
180+
break
181+
182+
return ans
183+
```
184+
</TabItem>
185+
</Tabs>
186+
187+
## Complexity Analysis
188+
189+
### Time Complexity: O(nlog(max(nums)))
190+
191+
### Space Complexity: O(N)

0 commit comments

Comments
 (0)