LeetCode 2429 - Minimize XOR
The problem is asking us to construct an integer x that satisfies two conditions. First, x must have the same number of set bits (1's in the binary representation) as a given integer num2. Second, the XOR of x with another integer num1 must be minimized.
Difficulty: 🟡 Medium
Topics: Greedy, Bit Manipulation
Solution
Problem Understanding
The problem is asking us to construct an integer x that satisfies two conditions. First, x must have the same number of set bits (1's in the binary representation) as a given integer num2. Second, the XOR of x with another integer num1 must be minimized. In other words, we want x XOR num1 to be as small as possible, which is equivalent to making x as close to num1 as possible in binary terms, but respecting the constraint on the number of set bits.
The inputs num1 and num2 are positive integers within the range [1, 10^9]. This implies that the maximum number of bits we need to consider is 30, since 2^30 is slightly larger than 10^9. The problem guarantees that the solution is unique, so we do not need to consider ties or multiple possible values of x.
Important edge cases include when num1 and num2 have the same number of set bits (then x = num1), when num2 has more set bits than num1 (we need to add bits), and when num2 has fewer set bits than num1 (we need to remove bits). Handling these carefully ensures the minimal XOR value.
Approaches
The brute-force approach would involve generating all integers that have the same number of set bits as num2 and computing x XOR num1 for each candidate. We would then select the integer with the minimum XOR. While this guarantees correctness, it is computationally infeasible. The number of integers with k set bits in a 30-bit space is C(30, k), which can be very large and would lead to combinatorial explosion.
The optimal approach leverages a greedy bit manipulation strategy. The key insight is that to minimize XOR with num1, we should try to align the set bits of x with the set bits of num1. This can be done in two steps: first, preserve as many set bits in num1 as possible, and second, if we still need more set bits, turn on the lowest unset bits. Conversely, if we need fewer set bits, we turn off the highest set bits. This ensures that x is the closest integer to num1 in terms of XOR while meeting the set bit requirement.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(2^n) | O(1) | Enumerates all numbers with same set bits as num2, infeasible for n ~ 30 |
| Optimal | O(30) | O(1) | Greedily set/unset bits based on num1 to match num2's set bit count |
Algorithm Walkthrough
- Count the number of set bits in
num2and store it intarget_bits. This is the number of 1'sxmust have. - Count the number of set bits in
num1and store it innum1_bits. - If
num1_bitsequalstarget_bits, thenx = num1and we are done. - If
num1_bitsis greater thantarget_bits, we need to turn off some bits innum1. Start from the least significant bit (LSB) and turn off 1's until we reach the required number of set bits. - If
num1_bitsis less thantarget_bits, we need to turn on additional bits. Start from the least significant bit and turn on 0's untilxhas the required number of set bits. - Return the resulting integer
x.
Why it works: By aligning the most significant bits of x with num1 as much as possible, we minimize XOR. If additional bits are needed, choosing the lowest unset bits ensures minimal increase in the XOR value. This greedy approach guarantees the XOR is minimized while satisfying the set bit count.
Python Solution
class Solution:
def minimizeXor(self, num1: int, num2: int) -> int:
target_bits = bin(num2).count('1')
num1_bits = bin(num1).count('1')
x = num1
if num1_bits > target_bits:
excess = num1_bits - target_bits
for i in range(31):
if excess == 0:
break
if (x >> i) & 1:
x ^= (1 << i)
excess -= 1
elif num1_bits < target_bits:
deficit = target_bits - num1_bits
for i in range(31):
if deficit == 0:
break
if not ((x >> i) & 1):
x |= (1 << i)
deficit -= 1
return x
This Python implementation counts set bits, compares the counts, and either removes or adds bits to match the target. It uses bitwise shifts and masks to manipulate individual bits efficiently. The loops are limited to 31 iterations due to the maximum 30-bit representation of num1 and num2.
Go Solution
func minimizeXor(num1 int, num2 int) int {
targetBits := 0
n := num2
for n > 0 {
targetBits += n & 1
n >>= 1
}
x := num1
num1Bits := 0
n = num1
for n > 0 {
num1Bits += n & 1
n >>= 1
}
if num1Bits > targetBits {
excess := num1Bits - targetBits
for i := 0; i < 31; i++ {
if excess == 0 {
break
}
if (x>>i)&1 == 1 {
x ^= 1 << i
excess--
}
}
} else if num1Bits < targetBits {
deficit := targetBits - num1Bits
for i := 0; i < 31; i++ {
if deficit == 0 {
break
}
if (x>>i)&1 == 0 {
x |= 1 << i
deficit--
}
}
}
return x
}
The Go implementation follows the same logic as Python, with explicit loops to count bits and manipulate individual bits using bitwise operators.
Worked Examples
Example 1: num1 = 3, num2 = 5
Binary representations: num1 = 0011, num2 = 0101.
target_bits = 2 (num2), num1_bits = 2. Since counts are equal, x = num1 = 3.
Example 2: num1 = 1, num2 = 12
Binary representations: num1 = 0001, num2 = 1100.
target_bits = 2 (num2), num1_bits = 1. Need to add 1 bit. Start from LSB, bit 1 is 0 → set it. Resulting x = 0011 = 3.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(30) | Loops over at most 31 bits for counting and manipulation |
| Space | O(1) | Only integer variables are used, no extra data structures |
The approach is extremely efficient because we only iterate over the fixed bit width of the input integers, not over their magnitude.
Test Cases
# Provided examples
assert Solution().minimizeXor(3, 5) == 3 # same number of bits
assert Solution().minimizeXor(1, 12) == 3 # need to add a bit
# Boundary and edge cases
assert Solution().minimizeXor(1, 1) == 1 # single bit, minimal XOR
assert Solution().minimizeXor(0, 1) == 1 # num1=0, need to add 1 bit
assert Solution().minimizeXor(15, 1) == 8 # num1 has 4 bits, num2 has 1 bit
assert Solution().minimizeXor(8, 15) == 15 # num1=1000, num2=1111, need to add 3 bits
| Test | Why |
|---|---|
3, 5 |
num1 already matches num2 in set bits |
1, 12 |
num1 has fewer bits than num2, need to add bits |
1, 1 |
minimal input, 1 bit |
0, 1 |
num1 zero, need to add 1 bit |
15, 1 |
num1 has excess bits, need to remove |
8, 15 |
num1 has fewer bits, need to add multiple bits |
Edge Cases
One edge case is when num1 and num2 already have the same number of set bits. A naive implementation might try to change bits unnecessarily, but the correct approach recognizes this equality and returns num1 immediately.
Another edge case occurs when num1 has more set bits than num2. The algorithm carefully iterates from the least significant bit upward to turn off