Skip to content

Coding patternsBlind 75

Reverse Bits

Mirror the 32 bits of a number, so bit 0 becomes bit 31. Solved two ways, moving one bit at a time in O(b) and swapping ever smaller blocks with masks in O(log b), with a step-through, code in five languages and the Big O worked out from the code.

You are given a 32-bit unsigned number number. Reverse the order of its 32 bits: bit 0 becomes bit 31, bit 1 becomes bit 30, and so on. Return the number those reversed bits make.

Example 1

Input: number = 10555190

Output: 1827702016

In hex, 0x00A10F36 becomes 0x6CF08500. The picture shows both, a byte per row. Leading zeros count: the 8 zeros on top move to the bottom.

input 31–24
00000000
input 23–16
10100001
input 15–8
00001111
input 7–0
00110110
output 31–24
01101100
output 23–16
11110000
output 15–8
10000101
output 7–0
00000000

Example 2

Input: number = 1

Output: 2147483648

The only 1 is bit 0. It moves to bit 31, which is 2³¹.

Example 3

Input: number = 4294967295

Output: 4294967295

All 32 bits are 1, so the mirror image looks the same.

  • 0 ≤ number ≤ 2³² − 1, and number always has exactly 32 bits, leading zeros included.
  • Below, b is the number of bits (32).
Do leading zeros count?
Yes. All 32 bits are reversed, so the zeros on top end up at the bottom.
How does Java or C# pass an unsigned 32-bit number?
Java has no unsigned int, so it passes the same 32 bits as an int, which may look negative. Use >>> to shift in zeros. C# can use uint.
What if the function is called many times?
That is the follow-up. The mask version does a fixed 5 steps with no loop. A table of reversed bytes also works.

The task moves bits around, not values. That is bit manipulation: number & 1 reads the lowest bit, shifts move bits, and a mask like 0xFF00FF00 picks out a group of bits at once.

This problem has two approaches. Turning number into a string, reversing it and parsing it back is the same bit-by-bit idea with extra memory.

Swap blocks O(log b)

Reversing a list is the same as swapping its two halves, then reversing each half. Do that to the bits, but for every block at once:

  1. Swap the two blocks of 16 bits.
  2. In each 16, swap the two blocks of 8.
  3. In each 8, swap the two blocks of 4.
  4. Then blocks of 2, then single bits.

A mask picks out the high block of every pair, and another picks the low block. Shift one right and the other left by the block size, and join them with |. One line swaps every pair.

number 31–24
00000000
number 23–16
10100001
number 15–8
00001111
number 7–0
00110110
  1. number = ((number >> 16) | (number << 16)) & 0xFFFFFFFF
  2. number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8)
  3. number = ((number & 0xF0F0F0F0) >> 4) | ((number & 0x0F0F0F0F) << 4)
  4. number = ((number & 0xCCCCCCCC) >> 2) | ((number & 0x33333333) << 2)
  5. number = ((number & 0xAAAAAAAA) >> 1) | ((number & 0x55555555) << 1)
  6. return number

number = 0x00A10F36. Instead of moving one bit at a time, swap the two halves, then the halves of each half, and so on. Five swaps reverse all 32 bits.

1 / 12
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// Swap blocks: swap the halves of 16 bits, then 8, 4, 2 and 1.
// log2(32) = 5 steps: O(log b) time, O(1) memory.
public int reverseBits(int number) {
// >>> shifts in zeros, because Java has no unsigned int.
number = (number >>> 16) | (number << 16);
// Each mask picks the high or the low block of every pair.
number = ((number & 0xFF00FF00) >>> 8) | ((number & 0x00FF00FF) << 8);
number = ((number & 0xF0F0F0F0) >>> 4) | ((number & 0x0F0F0F0F) << 4);
number = ((number & 0xCCCCCCCC) >>> 2) | ((number & 0x33333333) << 2);
number = ((number & 0xAAAAAAAA) >>> 1) | ((number & 0x55555555) << 1);
return number;
}
}
Time O(log b)Space O(1)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    Each lit line does two masks, two shifts and one | on a whole 32-bit word. The processor does each of those in one go, so each line is 1 step. There is no loop.

    1. number = ((number >> 16) | (number << 16)) & 0xFFFFFFFF
    2. number = ((number & 0xFF00FF00) >> 8) | ((number & 0x00FF00FF) << 8)
    3. number = ((number & 0xF0F0F0F0) >> 4) | ((number & 0x0F0F0F0F) << 4)
    4. number = ((number & 0xCCCCCCCC) >> 2) | ((number & 0x33333333) << 2)
    5. number = ((number & 0xAAAAAAAA) >> 1) | ((number & 0x55555555) << 1)
    6. return number
  2. 2 Count the runs on Example 1

    The block size starts at 16 and halves each step until it is 1.

    5 steps.

    step12345
    block size168421
    total
    5 steps
  3. 3 Write the count for any n

    The block size halves from b / 2 down to 1, so the number of steps is how many times b can be halved:

    log₂ b steps, and log₂ 32 = 5
  4. 4 Keep only the part that grows fastest

    There is nothing to drop. The count is log b. For b = 32 that is 5 steps against 32 runs. Both are O(1) for a fixed 32 bits, but this one has no loop and no branch.

    So the time is O(log b).

  5. S Space: count the extra memory the same way

    The code changes number in place and keeps nothing else. The space is O(1).

    number

2,000 random 32-bit numbers, with 0, single bits, the top bit and runs of ones mixed in. Bit by bit and swap blocks gave the same answer every time, and matched a check that reverses the binary text.

Largest inputsResult
One 32-bit number with ones at both ends. Swap blocks.Under 0.001 s, and under 0.001 MB of extra memory: it changes number in place.
Every single bit, all 32 of them. Both approaches.Each bit landed in its mirror place, the strongest check for the masks.