Advanced Bit Manipulation
Explore advanced bit manipulation techniques for solving complex algorithmic problems.
Single Number Problems
Single Number I
- Problem: Array where every number appears twice except one
- Solution: XOR all numbers
- Time Complexity: O(n)
- Space Complexity: O(1)
Single Number II
- Problem: Array where every number appears three times except one
- Solution: Count bits at each position, modulo 3
- Time Complexity: O(n)
- Space Complexity: O(1)
Single Number III
- Problem: Array where every number appears twice except two
- Solution: XOR to get difference, then separate groups
- Time Complexity: O(n)
- Space Complexity: O(1)
Missing Number Problems
Missing Number
- Problem: Array of n-1 numbers from 0 to n, find missing
- Solution: XOR all numbers with 0 to n
- Alternative: Sum formula
n*(n+1)/2 - sum
Missing Two Numbers
- Problem: Array of n-2 numbers from 1 to n, find missing two
- Solution: Use sum and sum of squares to get equations
Power of 2 Problems
Check Power of 2
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
Next Power of 2
int nextPowerOfTwo(int n) {
if (n == 0) return 1;
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n + 1;
}
Previous Power of 2
int prevPowerOfTwo(int n) {
if (n <= 1) return 0;
return 1 << (31 - __builtin_clz(n));
}
Bit Manipulation in Arrays
Maximum XOR of Two Numbers
- Problem: Find maximum XOR of any two numbers in array
- Solution: Use Trie or bit manipulation with prefix
Subarray XOR
- Problem: Find subarray with maximum XOR
- Solution: Use prefix XOR and Trie
Count Triplets with XOR Zero
- Problem: Count triplets (i, j, k) where arr[i] ^ arr[j] ^ arr[k] = 0
- Solution: Use property that a ^ b ^ c = 0 implies a ^ b = c
Bit Manipulation in Strings
Maximum XOR of Two Strings
- Problem: Find maximum XOR of two binary strings
- Solution: Greedy approach with bit manipulation
Gray Code
- Problem: Generate n-bit Gray code sequence
- Solution: Use formula
i ^ (i >> 1)
Hamming Distance
- Problem: Count positions where bits differ
- Solution: XOR and count set bits
Advanced Techniques
Bit Manipulation in Dynamic Programming
Subset Sum with Bitmask
// Check if subset sum equals target
bool canPartition(vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % 2) return false;
int target = sum / 2;
vector<bool> dp(target + 1, false);
dp[0] = true;
for (int num : nums) {
for (int i = target; i >= num; i--) {
dp[i] = dp[i] || dp[i - num];
}
}
return dp[target];
}
Traveling Salesman with Bitmask
// TSP using bitmask DP
int tsp(vector<vector<int>>& dist) {
int n = dist.size();
vector<vector<int>> dp(1 << n, vector<int>(n, INT_MAX));
dp[1][0] = 0; // Start at city 0
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u))) continue;
for (int v = 0; v < n; v++) {
if (mask & (1 << v)) continue;
int newMask = mask | (1 << v);
dp[newMask][v] = min(dp[newMask][v],
dp[mask][u] + dist[u][v]);
}
}
}
int result = INT_MAX;
for (int i = 1; i < n; i++) {
result = min(result, dp[(1 << n) - 1][i] + dist[i][0]);
}
return result;
}
Bit Manipulation in Graph Algorithms
Hamiltonian Path with Bitmask
// Check if Hamiltonian path exists
bool hasHamiltonianPath(vector<vector<int>>& graph) {
int n = graph.size();
for (int start = 0; start < n; start++) {
vector<vector<bool>> dp(1 << n, vector<bool>(n, false));
dp[1 << start][start] = true;
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u)) || !dp[mask][u]) continue;
for (int v : graph[u]) {
if (mask & (1 << v)) continue;
int newMask = mask | (1 << v);
dp[newMask][v] = true;
}
}
}
for (int i = 0; i < n; i++) {
if (dp[(1 << n) - 1][i]) return true;
}
}
return false;
}
Optimization Techniques
Fast Bit Counting
// Hamming weight using bit manipulation
int popcount(uint32_t n) {
n = n - ((n >> 1) & 0x55555555);
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
n = (n + (n >> 4)) & 0x0f0f0f0f;
n = n + (n >> 8);
n = n + (n >> 16);
return n & 0x3f;
}
Bit Reversal
// Reverse bits of a 32-bit integer
uint32_t reverseBits(uint32_t n) {
n = ((n & 0xffff0000) >> 16) | ((n & 0x0000ffff) << 16);
n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);
n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);
n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);
n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);
return n;
}
Common Patterns
- XOR Properties:
a ^ a = 0
,a ^ 0 = a
,a ^ b = b ^ a
- Bitmask Subsets: Generate all subsets using bitmasks
- State Compression: Use bits to represent visited states
- Prefix XOR: Use XOR prefix for range queries
- Trie with Bits: Use Trie for bit manipulation problems
Performance Considerations
- Use built-in functions when available (
__builtin_popcount
,__builtin_clz
) - Prefer bitwise operations over arithmetic when possible
- Consider cache efficiency when using large bitmasks
- Use appropriate data types (uint32_t, uint64_t) for bit operations