I’ve always sucked at bit manipulation problems so I read up on it and will go into stuff I learned and some of the problems I’ve done. Will try my best to keep this list regularly updated (no promises…)
I wont go into binary operations (AND, OR, XOR, LEFT and RIGHT BITSHIFT) since they are trivial, but I will go into what you can do with those.
- Setting the
ith bit ofxto 1:x |= (1 << i) - Clearing the
ith bit ofx:x &= ~(1 << i) - Toggling the
ith bit ofx(from 0 to 1 or 1 to 0):x ^= (1 << i) - Checking if the
ith bit is set: ifx & (1 << i) != 0, theith bit is set - Checking if a number
xis even or odd:if x & 1 == 0,xis even - Checking if a number is a power of 2: if
x > 0andx & (x - 1) == 0,xis a power of 2
Real life example
- In error detection and correction algorithms like checksums or parity bits, bit manipulation promotes data integrity during transmission by identifying and correcting errors in the binary data.
- IP addresses and subnet masks use bitwise AND operations to determine whether two devices are on the same network.
Implementations
Some implementation stuff from doing problems:
https://leetcode.com/problems/number-of-1-bits/
Hardware POPCNT: Modern CPUs have a dedicated hardware instruction designed specifically to count set bits, often called “population count” or POPCNT. In C++, compilers give you direct access to this via built-in functions. It often runs in a single CPU cycle. It is the absolute fastest way to count bits in C++.
Note: If n is a 64-bit long integer, you would use __builtin_popcountll(n) instead
https://stackoverflow.com/questions/57150666/how-does-builtin-popcount-of-gcc-work
class Solution {
public:
int hammingWeight(int n) {
return __builtin_popcount(n); // counts number of 1 bits
}
};
Brian Kernighan’s Algorithm: uses a neat bitwise trick: n &= (n - 1). If a number only has two 1s (like 10000000000000000000000000000010), the standard loop takes 32 steps. This algorithm takes exactly 2 steps. The time complexity drops from O(32) to O(k), where k is the number of set bits.
class Solution {
public:
int hammingWeight(int n) {
int cnt = 0;
while (n != 0) {
n &= (n - 1); // Clears the lowest '1' bit
cnt++;
}
return cnt;
}
};
https://leetcode.com/problems/single-number/description/
for this problem the key insight was that XOR is associative and commutative (order doesn’t matter: A ^ B ^ A is the same as A ^ A ^ B, which is 0 ^ B, which is B).
and the implementation can be simplified using std::accumulate. Also since C++20, std::accumulate can be marked as constexpr and be evaluated at compile time. This allows you to offload calculations to the compiler, leaving zero runtime overhead for the final output.
#include <numeric>
#include <functional>
class Solution {
public:
int singleNumber(vector<int>& nums) {
return std::accumulate(nums.begin(), nums.end(), 0, std::bit_xor<int>());
}
};
C++17 (Alternative introduced): While not a change to std::accumulate itself, C++17 introduced std::reduce, which functions similarly but allows for parallel execution.
https://leetcode.com/problems/single-number-iii/
This problem was also interesting since we needed to make use of 2s complement. 2s complement is a way which allows us to represent negative numbers in binary. How it works is you have a positive number, let’s say 10, convert it to binary. you will get: 00001010
according to 2s complement -10 will be represented as: ~x + 1 where its just invert bits of x + 1.
~10 becomes 11110101 and 11110101 + 1 becomes 11110110 (which is -10).
The reason this is important is because in the question we are supposed to identify 2 numbers which are unique in the array. The 2 numbers are unique (or else they would become 0 when xor’d). when we perform and operation between +6 and -6 (where 6 is the result of xor ops) for example, it will give us the bit which is different between the 2 unique numbers. For example:
...00000110 (6)
& ...11111010 (-6)
-----------------
...00000010 (2)
Our diff is 2 (binary 010). This tells us that our two unique numbers (3 and 5) have different values at the second bit from the right. From there the problem became straightforward.