Reverse Bits

Reverse the bits of a given 32-bit unsigned integer.

Language Selection

Choose your preferred programming language

Showing: Python

Reverse Bits

Problem Statement

Reverse bits of a given 32-bit unsigned integer.

Note:

  • Note that in some languages, such as Java, there is no unsigned integer type. In this case, both input and output will be signed integer types and 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 2 below, the input represents the signed integer -3 and the output represents the signed integer -1073741825.

Examples

Example 1:

Input: n = 00000010100101000001111010011100
Output: 00111001011110000010100101000000
Explanation: The input binary string 00000010100101000001111010011100 represents the unsigned integer 43261596, so return 964176192 which its binary representation is 00111001011110000010100101000000.

Example 2:

Input: n = 11111111111111111111111111111101
Output: 10111111111111111111111111111111
Explanation: The input binary string 11111111111111111111111111111101 represents the unsigned integer 4294967293, so return 3221225471 which its binary representation is 10111111111111111111111111111111.

Constraints

  • The input must be a binary string of length 32

Approach 1: Bit Manipulation (Optimal)

Algorithm

Extract bits one by one from the right and build the result from the left.

Steps:

  1. Initialize result to 0
  2. For each of the 32 bits:
    • Extract the rightmost bit from n
    • Add it to the result
    • Shift result left and n right
  3. Return the result

Implementation

Python:

def reverseBits(n):
    """
    Reverse bits of a 32-bit unsigned integer
    Time: O(1)
    Space: O(1)
    """
    result = 0
    
    for i in range(32):
        # Extract the rightmost bit
        bit = n & 1
        
        # Add it to the result
        result = (result << 1) | bit
        
        # Shift n right
        n >>= 1
    
    return result

Java:

public class Solution {
    /**
     * Reverse bits of a 32-bit unsigned integer
     * Time: O(1)
     * Space: O(1)
     */
    public int reverseBits(int n) {
        int result = 0;
        
        for (int i = 0; i < 32; i++) {
            // Extract the rightmost bit
            int bit = n & 1;
            
            // Add it to the result
            result = (result << 1) | bit;
            
            // Shift n right
            n >>= 1;
        }
        
        return result;
    }
}

Go:

func reverseBits(n uint32) uint32 {
    /**
     * Reverse bits of a 32-bit unsigned integer
     * Time: O(1)
     * Space: O(1)
     */
    var result uint32
    
    for i := 0; i < 32; i++ {
        // Extract the rightmost bit
        bit := n & 1
        
        // Add it to the result
        result = (result << 1) | bit
        
        // Shift n right
        n >>= 1
    }
    
    return result
}

JavaScript:

/**
 * Reverse bits of a 32-bit unsigned integer
 * Time: O(1)
 * Space: O(1)
 */
function reverseBits(n) {
    let result = 0;
    
    for (let i = 0; i < 32; i++) {
        // Extract the rightmost bit
        const bit = n & 1;
        
        // Add it to the result
        result = (result << 1) | bit;
        
        // Shift n right
        n >>>= 1; // Use unsigned right shift
    }
    
    return result;
}

C#:

public class Solution {
    /// <summary>
    /// Reverse bits of a 32-bit unsigned integer
    /// Time: O(1)
    /// Space: O(1)
    /// </summary>
    public uint reverseBits(uint n) {
        uint result = 0;
        
        for (int i = 0; i < 32; i++) {
            // Extract the rightmost bit
            uint bit = n & 1;
            
            // Add it to the result
            result = (result << 1) | bit;
            
            // Shift n right
            n >>= 1;
        }
        
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(1) - Exactly 32 iterations
  • Space Complexity: O(1) - Only using constant extra space

Approach 2: Lookup Table (Optimized)

Algorithm

Use a lookup table to reverse bytes, then reverse the order of bytes.

Steps:

  1. Create a lookup table for reversing 8-bit values
  2. Reverse each byte using the lookup table
  3. Reverse the order of the 4 bytes

Implementation

Python:

def reverseBits_lookup(n):
    """
    Reverse bits using lookup table
    Time: O(1)
    Space: O(1)
    """
    # Lookup table for reversing 8-bit values
    lookup = [0] * 256
    for i in range(256):
        lookup[i] = reverse8Bits(i)
    
    # Reverse each byte and reorder
    result = 0
    for i in range(4):
        byte = (n >> (i * 8)) & 0xFF
        result |= lookup[byte] << ((3 - i) * 8)
    
    return result

def reverse8Bits(n):
    """Reverse 8 bits"""
    result = 0
    for i in range(8):
        result = (result << 1) | (n & 1)
        n >>= 1
    return result

Java:

public class Solution {
    private static final int[] LOOKUP = new int[256];
    
    static {
        // Initialize lookup table
        for (int i = 0; i < 256; i++) {
            LOOKUP[i] = reverse8Bits(i);
        }
    }
    
    /**
     * Reverse bits using lookup table
     * Time: O(1)
     * Space: O(1)
     */
    public int reverseBitsLookup(int n) {
        int result = 0;
        
        // Reverse each byte and reorder
        for (int i = 0; i < 4; i++) {
            int byte = (n >> (i * 8)) & 0xFF;
            result |= LOOKUP[byte] << ((3 - i) * 8);
        }
        
        return result;
    }
    
    private static int reverse8Bits(int n) {
        int result = 0;
        for (int i = 0; i < 8; i++) {
            result = (result << 1) | (n & 1);
            n >>= 1;
        }
        return result;
    }
}

Go:

var lookup [256]uint32

func init() {
    // Initialize lookup table
    for i := 0; i < 256; i++ {
        lookup[i] = reverse8Bits(uint32(i))
    }
}

func reverseBitsLookup(n uint32) uint32 {
    /**
     * Reverse bits using lookup table
     * Time: O(1)
     * Space: O(1)
     */
    var result uint32
    
    // Reverse each byte and reorder
    for i := 0; i < 4; i++ {
        byte := (n >> (i * 8)) & 0xFF
        result |= lookup[byte] << ((3 - i) * 8)
    }
    
    return result
}

func reverse8Bits(n uint32) uint32 {
    var result uint32
    for i := 0; i < 8; i++ {
        result = (result << 1) | (n & 1)
        n >>= 1
    }
    return result
}

JavaScript:

// Lookup table for reversing 8-bit values
const lookup = new Array(256);
for (let i = 0; i < 256; i++) {
    lookup[i] = reverse8Bits(i);
}

/**
 * Reverse bits using lookup table
 * Time: O(1)
 * Space: O(1)
 */
function reverseBitsLookup(n) {
    let result = 0;
    
    // Reverse each byte and reorder
    for (let i = 0; i < 4; i++) {
        const byte = (n >> (i * 8)) & 0xFF;
        result |= lookup[byte] << ((3 - i) * 8);
    }
    
    return result;
}

function reverse8Bits(n) {
    let result = 0;
    for (let i = 0; i < 8; i++) {
        result = (result << 1) | (n & 1);
        n >>>= 1;
    }
    return result;
}

C#:

public class Solution {
    private static readonly uint[] Lookup = new uint[256];
    
    static Solution() {
        // Initialize lookup table
        for (int i = 0; i < 256; i++) {
            Lookup[i] = Reverse8Bits((uint)i);
        }
    }
    
    /// <summary>
    /// Reverse bits using lookup table
    /// Time: O(1)
    /// Space: O(1)
    /// </summary>
    public uint reverseBitsLookup(uint n) {
        uint result = 0;
        
        // Reverse each byte and reorder
        for (int i = 0; i < 4; i++) {
            uint byte = (n >> (i * 8)) & 0xFF;
            result |= Lookup[byte] << ((3 - i) * 8);
        }
        
        return result;
    }
    
    private static uint Reverse8Bits(uint n) {
        uint result = 0;
        for (int i = 0; i < 8; i++) {
            result = (result << 1) | (n & 1);
            n >>= 1;
        }
        return result;
    }
}

Complexity Analysis

  • Time Complexity: O(1) - Constant time with lookup table
  • Space Complexity: O(1) - Lookup table is constant size

Approach 3: Divide and Conquer (Alternative)

Algorithm

Use divide and conquer to reverse bits by swapping halves recursively.

Steps:

  1. Swap left and right 16-bit halves
  2. Swap left and right 8-bit halves within each 16-bit half
  3. Swap left and right 4-bit halves within each 8-bit half
  4. Swap left and right 2-bit halves within each 4-bit half
  5. Swap left and right 1-bit halves within each 2-bit half

Implementation

Python:

def reverseBits_divideConquer(n):
    """
    Reverse bits using divide and conquer
    Time: O(1)
    Space: O(1)
    """
    # Swap 16-bit halves
    n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16)
    
    # Swap 8-bit halves
    n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
    
    # Swap 4-bit halves
    n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
    
    # Swap 2-bit halves
    n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
    
    # Swap 1-bit halves
    n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
    
    return n

Java:

public class Solution {
    /**
     * Reverse bits using divide and conquer
     * Time: O(1)
     * Space: O(1)
     */
    public int reverseBitsDivideConquer(int n) {
        // Swap 16-bit halves
        n = ((n & 0xFFFF0000) >>> 16) | ((n & 0x0000FFFF) << 16);
        
        // Swap 8-bit halves
        n = ((n & 0xFF00FF00) >>> 8) | ((n & 0x00FF00FF) << 8);
        
        // Swap 4-bit halves
        n = ((n & 0xF0F0F0F0) >>> 4) | ((n & 0x0F0F0F0F) << 4);
        
        // Swap 2-bit halves
        n = ((n & 0xCCCCCCCC) >>> 2) | ((n & 0x33333333) << 2);
        
        // Swap 1-bit halves
        n = ((n & 0xAAAAAAAA) >>> 1) | ((n & 0x55555555) << 1);
        
        return n;
    }
}

Go:

func reverseBitsDivideConquer(n uint32) uint32 {
    /**
     * Reverse bits using divide and conquer
     * Time: O(1)
     * Space: O(1)
     */
    // Swap 16-bit halves
    n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16)
    
    // Swap 8-bit halves
    n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
    
    // Swap 4-bit halves
    n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
    
    // Swap 2-bit halves
    n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
    
    // Swap 1-bit halves
    n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
    
    return n
}

JavaScript:

/**
 * Reverse bits using divide and conquer
 * Time: O(1)
 * Space: O(1)
 */
function reverseBitsDivideConquer(n) {
    // Swap 16-bit halves
    n = ((n & 0xFFFF0000) >>> 16) | ((n & 0x0000FFFF) << 16);
    
    // Swap 8-bit halves
    n = ((n & 0xFF00FF00) >>> 8) | ((n & 0x00FF00FF) << 8);
    
    // Swap 4-bit halves
    n = ((n & 0xF0F0F0F0) >>> 4) | ((n & 0x0F0F0F0F) << 4);
    
    // Swap 2-bit halves
    n = ((n & 0xCCCCCCCC) >>> 2) | ((n & 0x33333333) << 2);
    
    // Swap 1-bit halves
    n = ((n & 0xAAAAAAAA) >>> 1) | ((n & 0x55555555) << 1);
    
    return n;
}

C#:

public class Solution {
    /// <summary>
    /// Reverse bits using divide and conquer
    /// Time: O(1)
    /// Space: O(1)
    /// </summary>
    public uint reverseBitsDivideConquer(uint n) {
        // Swap 16-bit halves
        n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16);
        
        // Swap 8-bit halves
        n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8);
        
        // Swap 4-bit halves
        n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4);
        
        // Swap 2-bit halves
        n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2);
        
        // Swap 1-bit halves
        n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1);
        
        return n;
    }
}

Complexity Analysis

  • Time Complexity: O(1) - Constant time bit operations
  • Space Complexity: O(1) - No extra space used

Key Insights

  1. Bit Extraction: Extract bits from right and build result from left
  2. Lookup Table: Pre-compute reversals for faster execution
  3. Divide and Conquer: Swap halves recursively
  4. Bit Masks: Use hexadecimal masks for efficient bit manipulation
  5. Unsigned Right Shift: Use >>> in JavaScript for unsigned shift

Edge Cases

  1. Zero: All zeros should reverse to all zeros
  2. All Ones: All ones should reverse to all ones
  3. Single Bit: Single set bit should reverse to single bit
  4. Alternating Pattern: 1010… should reverse to 0101…
  5. Large Numbers: Handle 32-bit integers correctly

Test Cases

def test_reverseBits():
    # Basic cases
    assert reverseBits(0b00000010100101000001111010011100) == 0b00111001011110000010100101000000
    assert reverseBits(0b11111111111111111111111111111101) == 0b10111111111111111111111111111111
    
    # Edge cases
    assert reverseBits(0) == 0
    assert reverseBits(0xFFFFFFFF) == 0xFFFFFFFF
    assert reverseBits(1) == 0x80000000
    assert reverseBits(0x80000000) == 1
    
    # Test all approaches
    n = 0b00000010100101000001111010011100
    assert reverseBits(n) == reverseBits_lookup(n)
    assert reverseBits(n) == reverseBits_divideConquer(n)
    
    print("All tests passed!")

test_reverseBits()

Follow-up Questions

  1. Reverse Bits in Range: Reverse only a specific range of bits?
  2. Reverse Bytes: Reverse the order of bytes in a 32-bit integer?
  3. Reverse Nibbles: Reverse the order of nibbles (4-bit groups)?
  4. Reverse Bits in String: Reverse bits in a binary string representation?
  5. Reverse Bits in Array: Reverse bits in an array of integers?

Common Mistakes

  1. Signed vs Unsigned: Not handling signed integers correctly
  2. Bit Shift Direction: Confusing left and right shifts
  3. Bit Mask Values: Incorrect hexadecimal mask values
  4. Loop Bounds: Not iterating exactly 32 times
  5. Result Initialization: Not initializing result to 0

Interview Tips

  1. Start Simple: Begin with the basic bit manipulation approach
  2. Explain Bit Operations: Show understanding of bit shifts and masks
  3. Discuss Optimization: Mention lookup table and divide-conquer approaches
  4. Handle Edge Cases: Mention zero, all ones, and single bit cases
  5. Follow-up Ready: Be prepared for variations and optimizations

Concept Explanations

Bit Manipulation Approach: The most straightforward approach extracts bits one by one from the rightmost position of the input and builds the result by shifting left and adding the extracted bit. This effectively reverses the bit order.

Lookup Table Optimization: Instead of reversing bits one by one, we can pre-compute the reversal of all possible 8-bit values and use these to reverse the 4 bytes of a 32-bit integer. This reduces the number of operations from 32 to 4.

Divide and Conquer: This approach uses the principle of divide and conquer by swapping halves of the bit string at different levels. We start by swapping 16-bit halves, then 8-bit halves, then 4-bit halves, and so on, until we’ve reversed all individual bits.

Bit Masks: Hexadecimal masks like 0xFFFF0000, 0xFF00FF00, etc., are used to select specific bit ranges for swapping. These masks are carefully chosen to isolate the bits we want to swap.

Why These Work: All approaches achieve the same result but with different trade-offs. The basic approach is simple and easy to understand, the lookup table is faster for repeated operations, and the divide-conquer approach is elegant and efficient.