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.
The problem
Section titled “The problem”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.
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.
Constraints
Section titled “Constraints”- 0 ≤ number ≤ 2³² − 1, and
numberalways has exactly 32 bits, leading zeros included. - Below, b is the number of bits (32).
Clarifying questions
Section titled “Clarifying questions”- 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.
Spot the pattern
Section titled “Spot the pattern”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.
Solutions
Section titled “Solutions”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.
Bit by bit O(b)
The idea
Section titled “The idea”Build the answer result one bit at a time. Each run, shift result left to make room, put the lowest bit
of number into that room, then shift number right to drop that bit. Do this exactly 32 times, even if number
becomes 0 early, so that leading zeros are counted.
The first bit taken from number is bit 0. It gets shifted left 31 more times, so it ends up as bit 31.
Walkthrough
Section titled “Walkthrough”- result = 0
- for _ in range(32):
- result = (result << 1) | (number & 1)
- number >>= 1
- return result
number = 10555190 (0x00A10F36). Take the lowest bit of number and push it onto the right of result, 32 times. The first bit taken ends up highest.
class Solution { // Bit by bit: push each bit of number onto the right of result. // 32 runs, even after number is 0: O(b) time, O(1) memory. public int reverseBits(int number) { int result = 0; // Run all 32 times, so the leading zeros move too. for (int k = 0; k < 32; k++) { result = (result << 1) | (number & 1); number >>>= 1; } return result; }}# Bit by bit: push each bit of number onto the right of result.# 32 runs, even after number is 0: O(b) time, O(1) memory.def reverse_bits(number: int) -> int: result = 0 # Run all 32 times, so the leading zeros move too. for _ in range(32): result = (result << 1) | (number & 1) number >>= 1 return resultpublic class Solution{ // Bit by bit: push each bit of number onto the right of result. // 32 runs, even after number is 0: O(b) time, O(1) memory. public uint ReverseBits(uint number) { uint result = 0; // Run all 32 times, so the leading zeros move too. for (int k = 0; k < 32; k++) { result = (result << 1) | (number & 1u); number >>= 1; } return result; }}// Bit by bit: push each bit of number onto the right of result.// 32 runs, even after number is 0: O(b) time, O(1) memory.func reverseBits(number uint32) uint32 { var result uint32 // Run all 32 times, so the leading zeros move too. for k := 0; k < 32; k++ { result = (result << 1) | (number & 1) number >>= 1 } return result}// Bit by bit: push each bit of number onto the right of result.// 32 runs, even after number is 0: O(b) time, O(1) memory.function reverseBits(number: number): number { let result = 0; // Run all 32 times, so the leading zeros move too. for (let k = 0; k < 32; k++) { result = (result << 1) | (number & 1); number >>>= 1; } // JS bit operators give signed 32-bit results; >>> 0 reads them as unsigned. return result >>> 0;}1 Find the work that repeats
The lit lines shift
result, read one bit, and shiftnumber. That is 1 step. They sit inside one loop.- result = 0
- for _ in range(32):
- result = (result << 1) | (number & 1)
- number >>= 1
- return result
2 Count the runs on Example 1
The loop runs once per bit of
number. The table groups the runs by byte.8 + 8 + 8 + 8 = 32 runs.
bits of number 0–7 8–15 16–23 24–31 runs 8 8 8 8 total8 + 8 + 8 + 8 = 32 runs3 Write the count for any n
The loop runs exactly once per bit, for every input:
b runs, and b = 32Check it with Example 1: 32 runs. Example 2 also takes 32, even though most of its bits are 0.
4 Keep only the part that grows fastest
There is nothing to drop. The count is b. Since b is fixed at 32, you can also call it O(1). Saying O(b) shows how it would grow with a wider number.
So the time is O(b).
S Space: count the extra memory the same way
The code keeps
resultandnumber. The space is O(1).resultnumber
Swap blocks O(log b)
The idea
Section titled “The idea”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:
- Swap the two blocks of 16 bits.
- In each 16, swap the two blocks of 8.
- In each 8, swap the two blocks of 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.
Walkthrough
Section titled “Walkthrough”- number = ((number >> 16) | (number << 16)) & 0xFFFFFFFF
- 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
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.
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; }}# 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.def reverse_bits(number: int) -> int: # Python ints never overflow, so cut the left shift back to 32 bits. number = ((number >> 16) | (number << 16)) & 0xFFFFFFFF # 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 numberpublic 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 uint ReverseBits(uint number) { number = (number >> 16) | (number << 16); // Each mask picks the high or the low block of every pair. number = ((number & 0xFF00FF00u) >> 8) | ((number & 0x00FF00FFu) << 8); number = ((number & 0xF0F0F0F0u) >> 4) | ((number & 0x0F0F0F0Fu) << 4); number = ((number & 0xCCCCCCCCu) >> 2) | ((number & 0x33333333u) << 2); number = ((number & 0xAAAAAAAAu) >> 1) | ((number & 0x55555555u) << 1); return number; }}// 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.func reverseBits(number uint32) uint32 { 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}// 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.function reverseBits(number: number): number { 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); // JS bit operators give signed 32-bit results; >>> 0 reads them as unsigned. return number >>> 0;}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.- number = ((number >> 16) | (number << 16)) & 0xFFFFFFFF
- 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
2 Count the runs on Example 1
The block size starts at 16 and halves each step until it is 1.
5 steps.
step 1 2 3 4 5 block size 16 8 4 2 1 total5 steps3 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 = 54 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).
S Space: count the extra memory the same way
The code changes
numberin place and keeps nothing else. The space is O(1).number
Tested at the limits
Section titled “Tested at the limits”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 inputs | Result |
|---|---|
| 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. |
The designbeyondcode digest, every Saturday
New deep dives, interview tips and site news in one short email. Free. Leave with one click.
We never sell or share your email. We only use it to send this newsletter.