147.Reverse Bits
Problem
Reverse the bits of a given 32-bit integer n: bit 0 swaps with bit 31, bit 1 with bit 30, and so on. Return the resulting integer.
All 32 positions take part, including leading zeros โ so a small number turns into a large one. In Java there is no unsigned int, so a result whose top bit is 1 is simply returned as a negative int with the correct bit pattern.
Examples
Input: n = 43261596
Output: 964176192
Explanation:
43261596 = 00000010100101000001111010011100
reversed = 00111001011110000010100101000000 = 964176192Input: n = 2147483644
Output: 1073741822
Explanation:
2147483644 = 01111111111111111111111111111100
reversed = 00111111111111111111111111111110 = 1073741822Constraints
0 <= n <= 2^31 - 2nis even.- The input must be treated as a 32-bit value (leading zeros count).
Follow-up: if this function is called many times, how would you optimize it?
๐ก Key Insight
Reversing is just moving bit i to position 31 - i. The simplest correct way is to pop bits off the right of n and push them onto the right of the result, 32 times. A faster fixed-cost way is divide and conquer: swap the two 16-bit halves, then the 8-bit halves inside each half, then 4-, 2- and 1-bit groups โ five mask-and-shift steps reverse all 32 bits.
Brute Force (String Reversal)
TimeO(32) = O(1), but with string allocation and parsing overhead.SpaceO(32) = O(1) for the strings.Intuition
Turn the number into its 32-character binary string (padding with leading zeros), reverse the string, and parse it back as an unsigned base-2 number.
Algorithm
s = Integer.toBinaryString(n), left-padded with'0'to length 32.- Reverse
s. - Return
Integer.parseUnsignedInt(reversed, 2)(handles results with the top bit set).
Visualization
n = 43261596
padded : 00000010100101000001111010011100
reversed: 00111001011110000010100101000000 โ 964176192Code
class Solution {
public int reverseBits(int n) {
String bits = Integer.toBinaryString(n);
// pad with leading zeros so all 32 positions are reversed
String padded = "0".repeat(32 - bits.length()) + bits;
String reversed = new StringBuilder(padded).reverse().toString();
// parseUnsignedInt accepts values up to 2^32 - 1 (top bit set โ negative int)
return Integer.parseUnsignedInt(reversed, 2);
}
}Complexity
- Time: O(32) = O(1), but with string allocation and parsing overhead.
- Space: O(32) = O(1) for the strings.
Bit by Bit
TimeO(32) = O(1)SpaceO(1).Intuition
Read n from its least significant bit upward, and build result by shifting it left and appending each bit. After 32 steps, the bit that was read first (bit 0) has been shifted all the way to bit 31.
Algorithm
result = 0.- Repeat 32 times:
result = (result << 1) | (n & 1)โ appendn's lowest bit.n >>>= 1โ move to the next bit.
- Return
result.
Dry Run (8-bit illustration)
n = 1011 0010, result = 0000 0000
step 1: bit 0 โ result = 0000 0000 n = 0101 1001
step 2: bit 1 โ result = 0000 0001 n = 0010 1100
step 3: bit 0 โ result = 0000 0010 n = 0001 0110
step 4: bit 0 โ result = 0000 0100 n = 0000 1011
step 5: bit 1 โ result = 0000 1001 n = 0000 0101
step 6: bit 1 โ result = 0001 0011 n = 0000 0010
step 7: bit 0 โ result = 0010 0110 n = 0000 0001
step 8: bit 1 โ result = 0100 1101 โ reversed!Code
class Solution {
public int reverseBits(int n) {
int result = 0;
for (int i = 0; i < 32; i++) {
result = (result << 1) | (n & 1); // append n's lowest bit
n >>>= 1; // unsigned shift to the next bit
}
return result;
}
}Complexity
- Time: O(32) = O(1) โ exactly 32 iterations.
- Space: O(1).
Divide and Conquer with MasksOptimal
TimeO(1)SpaceO(1).Intuition
Reversing a sequence equals reversing the order of its two halves and then reversing each half. Apply that recursively, but on all groups in parallel using masks:
- Swap the 16-bit halves.
- Swap adjacent 8-bit bytes (mask
0xff00ff00/0x00ff00ff). - Swap adjacent 4-bit nibbles (
0xf0f0f0f0/0x0f0f0f0f). - Swap adjacent 2-bit pairs (
0xcccccccc/0x33333333). - Swap adjacent single bits (
0xaaaaaaaa/0x55555555).
Each step keeps the "high" group, shifts it right, keeps the "low" group, shifts it left, and ORs them. Five steps instead of 32 โ which is the answer to the follow-up (alternatively, cache the reversal of each byte in a 256-entry table).
Algorithm
n = (n >>> 16) | (n << 16).- For each
(mask, shift)in(0xff00ff00, 8),(0xf0f0f0f0, 4),(0xcccccccc, 2),(0xaaaaaaaa, 1):n = ((n & mask) >>> shift) | ((n & ~mask) << shift). - Return
n.
Dry Run
The same idea on 8 bits (three swap levels instead of five):
flowchart TD
A["n = 1011 0010"] -->|"swap 4-bit halves"| B["0010 1011"]
B -->|"swap 2-bit pairs<br/>00 10 10 11 โ 10 00 11 10"| C["1000 1110"]
C -->|"swap single bits<br/>10 00 11 10 โ 01 00 11 01"| D["0100 1101"]
D --> E["reversed of 1011 0010"]Code
class Solution {
public int reverseBits(int n) {
n = (n >>> 16) | (n << 16); // swap 16-bit halves
n = ((n & 0xff00ff00) >>> 8) | ((n & 0x00ff00ff) << 8); // swap bytes
n = ((n & 0xf0f0f0f0) >>> 4) | ((n & 0x0f0f0f0f) << 4); // swap nibbles
n = ((n & 0xcccccccc) >>> 2) | ((n & 0x33333333) << 2); // swap bit pairs
n = ((n & 0xaaaaaaaa) >>> 1) | ((n & 0x55555555) << 1); // swap single bits
return n;
}
}Complexity
- Time: O(1) โ five constant mask/shift steps (this is what
Integer.reversedoes internally). - Space: O(1).
Complexity Comparison
| Approach | Time | Space |
|---|---|---|
| String Reversal | O(32) + string overhead | O(32) |
| Bit by Bit | O(32) | O(1) |
| Divide and Conquer Masks | O(5) | O(1) |
Common Pitfalls
- Stopping the loop when
n == 0โ leading zeros must still be shifted intoresult, so always run all 32 iterations. - Using
>>instead of>>>in the mask approach โ an arithmetic shift copies the sign bit into the vacated positions and corrupts the result for negative intermediates. - Using
Integer.parseInt(s, 2)on a 32-bit string that starts with1โ it throwsNumberFormatException; useparseUnsignedInt. - Forgetting to pad
Integer.toBinaryStringto 32 characters โ it drops leading zeros, so you would reverse too few bits. - Panicking at a negative output in Java โ the bit pattern is what matters; a set top bit simply prints as a negative
int.