Loading W Code...
Master bitwise operators, masks, and low-level optimizations.
6
Topics
45
Minutes
O(1)
Tricks
Imagine a panel of simple light switches. Each switch can only be ON (1) or OFF (0). In computer systems, all data is ultimately boiled down to these binary switches (bits) because transistors only recognize two physical states: current flowing or blocked.
Bit Manipulation is the practice of performing operations directly on these individual bits. It is the fastest possible computation method because it maps directly to hardware instructions.
&): Compares bits and returns 1 only if both inputs are 1.
101 & 011 = 001|): Compares bits and returns 1 if at least one input is 1.
101 | 011 = 111^): Compares bits and returns 1 only if the inputs are different.
101 ^ 011 = 110~): Inverts all bits (flips 1 to 0 and vice versa).
~101 = ...11010<<): Shifts bits to the left, filling empty spaces on the right with 0. This effectively multiplies the number by 2 for each shift position.
5 << 1 (binary 101 becomes 1010, which is 10).>>): Shifts bits to the right, discarding truncated bits on the far right. This effectively divides the integer by 2 (rounding down).
5 >> 1 (binary 101 becomes 10, which is 2).int a = 5, b = 3;
cout << (a & b); // 1
cout << (a | b); // 7
cout << (a ^ b); // 6
cout << (~a); // -6
cout << (a << 1); // 10
cout << (a >> 1); // 2These bitwise shortcuts bypass standard conditional blocks, offering high performance in low-latency systems:
Evaluating if the least significant bit (LSB) is set. Odd numbers always end in 1, while even numbers always end in 0.
n & 1 (returns 1 if odd, 0 if even).Force the i-th bit to become 1 without altering any other bits.
n | (1 << i)Force the i-th bit to become 0 without altering any other bits.
n & ~(1 << i)Flip the i-th bit (if it is 1 it becomes 0; if it is 0 it becomes 1).
n ^ (1 << i)Instantly reset the lowest 1 bit to 0. This is a powerful trick used to count set bits or verify binary powers.
n & (n - 1)bool isOdd(int n) { return n & 1; }
int setBit(int n, int i) { return n | (1 << i); }
int clearBit(int n, int i) { return n & ~(1 << i); }
int toggleBit(int n, int i) { return n ^ (1 << i); }Integers that represent a power of 2 (e.g., 2, 4, 8, 16, 32) have exactly one 1 bit in their binary format (e.g., 4 is 0100, 8 is 1000).
O(1) Time)If you subtract 1 from a power of two, all bits to the right flip (e.g., 8 - 1 = 7, which is 0111). Performing an AND operation between n and n-1 will cancel the lone set bit:
n > 0 && (n & (n - 1)) == 0Instead of checking all 32 bits one by one, Brian Kernighan's method jumps directly from set bit to set bit by repeatedly applying n = n & (n - 1) until n becomes 0.
O(k) steps, where k is the exact number of set bits.bool isPowerOfTwo(int n) {
return n > 0 && ((n & (n - 1)) == 0);
}
int countSetBits(int n) {
int count = 0;
while (n > 0) {
n = n & (n - 1);
count++;
}
return count;
}The XOR (^) operator has unique algebraic properties that make it a favorite in coding interviews:
x ^ x = 0 (XORing a value with itself yields zero).x ^ 0 = x (XORing any value with zero leaves it unchanged).Given an array where every integer appears exactly twice except for one unique element, find that unique element.
0 (x ^ x = 0), leaving only the single unique value.You can swap two integers a and b without allocating any temporary helper variable using XOR steps:
a = a ^ bb = a ^ ba = a ^ bint singleNumber(vector<int>& nums) {
int res = 0;
for (int x : nums) res ^= x;
return res;
}Generating all subsets of a collection (the Power Set) is a classic problem that can be solved elegantly using bitmask counters.
A set containing n elements generates exactly 2ⁿ subsets. We can map each subset directly to a binary index ranging from 0 to 2ⁿ - 1.
mask from 0 to 2ⁿ - 1.j-th bit. If the bit is set (1), we include the j-th element in the current subset.nums = [A, B, C], mask 5 (binary 101) represents the subset [A, C].vector<vector<int>> subsets(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> res;
for (int mask = 0; mask < (1 << n); mask++) {
vector<int> sub;
for (int j = 0; j < n; j++) {
if (mask & (1 << j)) {
sub.push_back(nums[j]);
}
}
res.push_back(sub);
}
return res;
}Problem: Find the maximum XOR value that can be formed by picking any two numbers from an array.
O(32 * n) Time)Instead of comparing every pair (O(n²) brute force), we construct the optimal maximum XOR result bit-by-bit from the most significant bit (MSB) to the least significant bit (LSB):
i, we record all prefixes in a Hash Set.1 at the current bit position. We assume a target maximum candidate: greedyTry = currentMax | (1 << i).A ^ B = C, then A ^ C = B), we check if there exists a prefix in our set such that prefix ^ greedyTry is also present in the set. If true, we update our maximum.int findMaximumXOR(vector<int>& nums) {
int maxResult = 0, mask = 0;
for (int i = 31; i >= 0; i--) {
mask = mask | (1 << i);
unordered_set<int> prefixes;
for (int num : nums) prefixes.insert(num & mask);
int greedyTry = maxResult | (1 << i);
for (int prefix : prefixes) {
if (prefixes.count(greedyTry ^ prefix)) {
maxResult = greedyTry;
break;
}
}
}
return maxResult;
}