A clear explanation of counting set bits in an integer using bit manipulation and Brian Kernighan's algorithm.
Problem Restatement
We are given a positive integer n.
We need to return the number of 1 bits in its binary representation. This count is also called the Hamming weight. The official problem asks for the number of set bits in the binary representation of n.
A set bit means a bit whose value is 1.
For example:
n = 11
binary = 1011There are three 1 bits, so the answer is:
3Input and Output
| Item | Meaning |
|---|---|
| Input | A positive integer n |
| Output | Number of 1 bits in the binary representation |
| Name | Hamming weight |
| Main operation | Bit counting |
Example function shape:
def hammingWeight(n: int) -> int:
...Examples
Example 1:
n = 11Binary:
1011The 1 bits are:
1 0 1 1
^ ^ ^There are 3 set bits.
Output:
3Example 2:
n = 128Binary:
10000000There is only one 1 bit.
Output:
1Example 3:
n = 2147483645This number has 30 set bits.
Output:
30First Thought: Check Every Bit
A simple way is to repeatedly check the last bit.
The expression:
n & 1returns 1 if the last bit is 1, otherwise it returns 0.
Then we shift n right by one bit:
n >>= 1This removes the last bit and moves the next bit into place.
That approach works.
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n:
count += n & 1
n >>= 1
return countThis checks every bit position until n becomes zero.
Key Insight
There is a faster bit trick.
The expression:
n & (n - 1)removes the rightmost 1 bit from n.
Example:
n = 1100
n - 1 = 1011
n&(n-1) = 1000The rightmost 1 in 1100 was removed.
So instead of checking every bit, we can remove one set bit at a time and count how many removals happen.
For example:
n = 1011First removal:
1011 & 1010 = 1010Second removal:
1010 & 1001 = 1000Third removal:
1000 & 0111 = 0000We removed three 1 bits, so the answer is 3.
Algorithm
- Set
count = 0. - While
nis not zero:- Replace
nwithn & (n - 1). - Increase
countby1.
- Replace
- Return
count.
Each loop removes exactly one set bit.
Correctness
At every iteration, n & (n - 1) removes the lowest set bit of n.
So each iteration reduces the number of 1 bits by exactly one.
The loop stops when n becomes zero.
A number is zero exactly when it has no 1 bits left.
Therefore, the number of iterations is exactly the number of original set bits in n.
Since count increases once per removed set bit, the final count equals the Hamming weight of the original number.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(b) | The loop runs once per set bit |
| Space | O(1) | Only a counter is stored |
Here, b is the number of 1 bits in n.
For a 32-bit integer, this is also O(1) because there are at most 32 bits.
Implementation
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
while n:
n &= n - 1
count += 1
return countCode Explanation
We start with zero counted bits:
count = 0The loop continues while some set bit still exists:
while n:This line removes the rightmost set bit:
n &= n - 1After removing one set bit, we count it:
count += 1When n becomes zero, all set bits have been removed, so we return:
return countAlternative Implementation: Fixed 32-Bit Scan
This version checks all 32 bit positions.
class Solution:
def hammingWeight(self, n: int) -> int:
count = 0
for _ in range(32):
count += n & 1
n >>= 1
return countThis is also valid for a 32-bit input. It always performs exactly 32 iterations.
The n & (n - 1) version can use fewer iterations when the number has only a few set bits.
Testing
def run_tests():
s = Solution()
assert s.hammingWeight(11) == 3
assert s.hammingWeight(128) == 1
assert s.hammingWeight(2147483645) == 30
assert s.hammingWeight(1) == 1
assert s.hammingWeight(0) == 0
assert s.hammingWeight(15) == 4
print("all tests passed")
run_tests()Test meaning:
| Test | Why |
|---|---|
11 | Binary 1011, main small example |
128 | Single set bit |
2147483645 | Large input with many set bits |
1 | Smallest positive set bit case |
0 | No set bits |
15 | Binary 1111, all low bits set |