191. Number of 1 Bits

The Problem

Link to original problem on Leetcode.

Write a function that takes an unsigned integer and returns the number of 1 bits it has (also known as the Hamming weight).

Notes
  • Note that in some languages, such as Java, there is no unsigned integer type. In this case, the input will be given as a signed integer type. It should not affect your implementation, as the integer's internal binary representation is the same, whether it is signed or unsigned.
  • In Java, the compiler represents the signed integers using 2's complement notation. Therefore, in Example 3, the input represents the signed integer. -3.
Examples

Example 1:

Input: n = 00000000000000000000000000001011
Output: 3
Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits.

Example 2:

Input: n = 00000000000000000000000010000000
Output: 1
Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit.

Example 3:

Input: n = 11111111111111111111111111111101
Output: 31
Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.
Constraints
  • The input must be a binary string of length 32.

My Solution

So, first of all, leetcode lies—it does not pass a binary string to the function (at least not when I write it in JavaScript), but passes a regular 'ole number. Had they actually used a string, I would've first converted it to an integer with parseInt(n, 2), where n is the binary string and 2 is the radix, or numeral base, I want to convert to.1

A naïve way to solve this would be to use bit shifting. We could compare n to 1 with AND, add the result to a counter, then shift n to the right. This will examine each bit in the number. Take 1011 for example.

count = 0

  1011
& 0001
------
  0001 => add 1 to count

  0101
& 0001
------
  0001 => add 1 to count

  0010
& 0001
------
  0000 => add 0 to count

  0001
& 0001
------
  0001 => add 1 to count

count === 3

In code:

const hammingWeight = (n) => {
let count = 0;
while (n) {
count += n & 1;
// Note: in JavaScript, use >>> for unsigned binary
// integer shifting. For a signed 32-bit integer, a
// negative number is marked with a 1 in the first
// position. Using >> will preserve that one, causing
// an infinite loop!
n = n >>> 1;
}
return count;
};

Another more efficient way to solve this problem is by comparing n with n - 1 using AND. Every time you do this, mutate n to equal the result and increment a counter until n equals 0. What this does is clear the least significant digit with each iteration, meaning we only iterate as many times as there are 1's. Here's how this would look with the example 1011.

First loop
  1011 => n, which is 11
& 1010 => n - 1, which is 10
------
  1010 => set n to this
  count += 1

Second loop
  1010 => n
& 1001 => n - 1
------
  1000 => set n to this
  count += 1

Third loop
  1000 => n
& 0111 => n - 1
------
  0000 => n === 0, we can stop after this loop
  count += 1

Once n is zero, we've incremented our count variable once for each instance of a 1 in the binary representation of n. Each time we mutated n, we were setting it to it's previous binary value but with the smallest 1 bit set to 0 instead. (E.g., 1011 to 1010 to 1000 and finally to 0000.) In code:

const hammingWeight = (n) => {
let count = 0;
while (n) {
n = n & (n - 1);
count++;
}
return count;
};

You can also write this recursively:

const hammingWeight = (n) => {
return n ? hammingWeight(n & (n - 1)) : 0;
}

  1. Ok, now I'm lying. If it were a string, I would've just written n.split('').reduce((p, c) => c === '1' ? p + 1 : p, 0) and called it a day. ↩︎