AlgoAtlas

Bit Manipulation

Phase 3 10 solutions

Bit Manipulation

mindmap
  root((Bit Manipulation))
    XOR Tricks
      Single Number LC136
      Missing Number LC268
      Sum of Two Integers LC371
    Bit Counting
      Number of 1 Bits LC191
      Counting Bits LC338
      Bitwise AND of Numbers Range LC201
    Bit Construction
      Reverse Bits LC190
      Minimum Array End LC3133
    Arithmetic
      Add Binary LC67
      Reverse Integer LC7

Problem List

# Problem LC # Difficulty Key Technique
1 Single Number 136 Easy XOR all elements
2 Number of 1 Bits 191 Easy Brian Kernighan's
3 Counting Bits 338 Easy DP with bit shift
4 Add Binary 67 Easy Bit-by-bit addition
5 Reverse Bits 190 Easy Shift and OR
6 Missing Number 268 Easy XOR or Gauss sum
7 Sum of Two Integers 371 Medium XOR + AND carry
8 Reverse Integer 7 Medium Mod + overflow check
9 Bitwise AND of Numbers Range 201 Medium Common prefix
10 Minimum Array End 3133 Medium Bit construction

Bit Manipulation Tricks Cheat Sheet

graph TD
    B[Bit Tricks] --> X[XOR]
    B --> A[AND]
    B --> S[Shifts]
    B --> O[OR]

    X --> X1["a XOR a = 0"]
    X --> X2["a XOR 0 = a"]
    X --> X3["XOR all → find unique"]

    A --> A1["n AND n-1 → clear lowest set bit"]
    A --> A2["n AND 1 → check odd/even"]
    A --> A3["range AND → common prefix"]

    S --> S1["n >> 1 → divide by 2"]
    S --> S2["n << 1 → multiply by 2"]
    S --> S3["dp[i] = dp[i>>1] + (i&1)"]

    O --> O1["a OR b → set bits from both"]
    O --> O2["used in bit-by-bit addition carry"]

XOR Properties and Applications

flowchart TD
    A[XOR Properties] --> B["Identity: a XOR 0 = a"]
    A --> C["Self-inverse: a XOR a = 0"]
    A --> D["Commutative & Associative"]

    B --> E[Single Number #136]
    E --> F["XOR all nums → pairs cancel → unique remains"]

    C --> G[Missing Number #268]
    G --> H["XOR 0..n XOR all nums → missing remains"]

    D --> I[Sum of Two Integers #371]
    I --> J["sum = a XOR b, carry = a AND b shifted left, repeat"]

    style F fill:#2d6a4f,color:#fff
    style H fill:#2d6a4f,color:#fff
    style J fill:#2d6a4f,color:#fff

Brian Kernighan's Algorithm Flow

flowchart TD
    A[count = 0, n = input] --> B{n != 0?}
    B -- No --> E[Return count]
    B -- Yes --> C["n = n AND n-1  ← clears lowest set bit"]
    C --> D[count += 1]
    D --> B

    style A fill:#2d6a4f,color:#fff
    style E fill:#1b4332,color:#fff

Each iteration removes exactly one set bit → O(number of set bits)

Counting Bits (LC 338) DP shortcut: dp[i] = dp[i >> 1] + (i & 1)


Bit-by-Bit Addition Flow (No + Operator)

flowchart TD
    A["a = x, b = y"] --> B{b != 0?}
    B -- No --> F[Return a]
    B -- Yes --> C["sum_bits = a XOR b  ← add without carry"]
    C --> D["carry = a AND b shifted left 1  ← carry positions"]
    D --> E["a = sum_bits, b = carry"]
    E --> B

    style A fill:#2d6a4f,color:#fff
    style F fill:#1b4332,color:#fff

Terminates when no carry remains. Used in: Sum of Two Integers #371


Common Prefix — Range AND Flow

flowchart TD
    A["left, right"] --> B{left != right?}
    B -- No --> E["Return left  ← common prefix"]
    B -- Yes --> C["left = left >> 1"]
    C --> D["right = right >> 1, shift_count++"]
    D --> B
    E --> F["Return left << shift_count"]

    style A fill:#2d6a4f,color:#fff
    style F fill:#1b4332,color:#fff

All bits that differ between left and right become 0 in the AND of the range.


Complexity Summary

Problem Time Space
Single Number O(n) O(1)
Number of 1 Bits O(log n) O(1)
Counting Bits O(n) O(n)
Add Binary O(n) O(n)
Reverse Bits O(1) — 32 iters O(1)
Missing Number O(n) O(1)
Sum of Two Integers O(1) — 32 iters O(1)
Reverse Integer O(log n) O(1)
Bitwise AND of Range O(log n) O(1)
Minimum Array End O(log n) O(1)

Study Order

  1. Single Number — XOR identity
  2. Missing Number — XOR or Gauss
  3. Number of 1 Bits — Brian Kernighan's
  4. Counting Bits — DP with right shift
  5. Reverse Bits — shift and OR
  6. Add Binary — string bit addition
  7. Sum of Two Integers — XOR + carry loop
  8. Reverse Integer — overflow handling
  9. Bitwise AND of Numbers Range — common prefix
  10. Minimum Array End — bit construction

Bit Manipulation

Core Intuition

Operate directly on binary representations. Key tricks:

  • XOR: a ^ a = 0, a ^ 0 = a → cancels duplicates, finds missing numbers
  • AND: n & (n-1) clears lowest set bit; n & (-n) isolates lowest set bit
  • Shift: n >> 1 divides by 2; 1 << k creates a mask for bit k
  • Two's complement: -n = ~n + 1; negative numbers have all high bits set

Decision Flowchart

flowchart TD
    A["Bit Manipulation Problem"] --> B{"What's the goal?"}
    B -->|Find unique element| C["XOR all elements<br/>duplicates cancel<br/>Single Number"]
    B -->|Count set bits| D{Single number or array?}
    D -->|Single| E["n AND (n-1) loop<br/>Number of 1 Bits"]
    D -->|Array 0..n| F["DP: bits[i] = bits[i>>1] + (i AND 1)<br/>Counting Bits"]
    B -->|Find missing number| G["XOR with indices 0..n<br/>Missing Number"]
    B -->|Arithmetic without operators| H["XOR for sum bits<br/>AND+shift for carry<br/>Sum Two Integers"]
    B -->|Reverse bits| I["Extract LSB, shift into result<br/>Reverse Bits"]
    B -->|String binary addition| J["Simulate with carry<br/>Add Binary"]
    B -->|Range AND| K["Right-shift until equal<br/>Bitwise AND Range"]
    B -->|Construct number| L["Bit-by-bit construction<br/>Min Array End"]

Problems

Single Number — LC 136 | Easy

  • Learn: XOR all numbers; pairs cancel to 0, leaving the unique element
  • Mistake: Using a hash set (O(n) space); XOR is O(1) space
  • Complexity: O(n) time, O(1) space
  • Edge: Single element array → that element; all pairs present

Number of 1 Bits — LC 191 | Easy

  • Learn: n & (n-1) clears the lowest set bit; count iterations until n=0
  • Mistake: Using string conversion bin(n).count('1') (valid but not bit manipulation)
  • Complexity: O(k) time where k = number of set bits, O(1) space
  • Edge: n=0 → 0; n=2^31-1 → 32 (all bits set)

Counting Bits — LC 338 | Easy

  • Learn: dp[i] = dp[i >> 1] + (i & 1) — right shift removes LSB, add 1 if LSB was set
  • Mistake: Calling popcount for each number separately (O(n log n) vs O(n))
  • Complexity: O(n) time, O(n) space
  • Edge: n=0 → [0]; relies on dp[0]=0 base case

Add Binary — LC 67 | Easy

  • Learn: Simulate addition from right with carry; use two pointers on both strings
  • Mistake: Converting to int (overflow for very long binary strings)
  • Complexity: O(max(m,n)) time, O(max(m,n)) space
  • Edge: Different length strings; carry propagates past the longer string ("1"+"1"→"10")

Reverse Bits — LC 190 | Easy

  • Learn: Extract LSB with n & 1, shift into result from MSB position; repeat 32 times
  • Mistake: Not doing exactly 32 iterations (leading zeros matter for 32-bit representation)
  • Complexity: O(1) time (fixed 32 bits), O(1) space
  • Edge: n=0 → 0; n=1 → 2^31 (bit moves to MSB)

Missing Number — LC 268 | Easy

  • Learn: XOR indices 0..n with all array values; missing number remains. Or: expected_sum - actual_sum
  • Mistake: Using sort O(n log n) when O(n) XOR or math works
  • Complexity: O(n) time, O(1) space
  • Edge: Missing 0; missing n (last element); single element array

Sum of Two Integers — LC 371 | Medium

  • Learn: a ^ b gives sum without carry; (a & b) << 1 gives carry; repeat until carry=0
  • Mistake: Infinite loop with negative numbers in Python (arbitrary precision integers); mask with 0xFFFFFFFF
  • Complexity: O(1) time (max 32 iterations), O(1) space
  • Edge: One operand is 0; negative numbers (Python needs masking); a = -b → 0

Reverse Integer — LC 7 | Medium

  • Learn: Extract digits with % 10, build reversed number with * 10; check overflow before each step
  • Mistake: Reversing string then converting (misses overflow check); checking overflow after (too late)
  • Complexity: O(log n) time, O(1) space
  • Edge: Negative numbers (preserve sign); trailing zeros become leading zeros; overflow → return 0

Bitwise AND of Numbers Range — LC 201 | Medium

  • Learn: AND of a range = common prefix of a and b in binary; right-shift both until equal, then shift back
  • Mistake: Iterating through all numbers in range (TLE for large ranges)
  • Complexity: O(log n) time, O(1) space
  • Edge: a=0 → 0 (AND with 0 is always 0); a=b → return a

Minimum Array End — LC 3133 | Medium

  • Learn: Start with x; OR in bits of (n-1) into the zero-bit positions of x to construct the minimum valid end
  • Mistake: Incrementing from x and checking conditions (too slow for large n)
  • Algorithm: Spread bits of (n-1) into the 0-positions of x using bit-by-bit construction
  • Complexity: O(log n) time, O(1) space
  • Edge: n=1 → x itself; x=0 → n-1

General Edge Cases

  • n=0 (XOR and bit count edge cases)
  • Negative numbers (two's complement; Python needs & 0xFFFFFFFF masking)
  • 32-bit overflow (Reverse Integer, Sum Two Integers)
  • All bits set (0xFFFFFFFF)
  • Single bit set vs all bits set
  • Range where left=right (Bitwise AND)
# Problem LC # Difficulty Key Technique
1 Single Number 136 Easy XOR all elements
2 Number of 1 Bits 191 Easy Brian Kernighan's
3 Counting Bits 338 Easy DP with bit shift
4 Add Binary 67 Easy Bit-by-bit addition
5 Reverse Bits 190 Easy Shift and OR
6 Missing Number 268 Easy XOR or Gauss sum
7 Sum of Two Integers 371 Medium XOR + AND carry
8 Reverse Integer 7 Medium Mod + overflow check
9 Bitwise AND of Numbers Range 201 Medium Common prefix
10 Minimum Array End 3133 Medium Bit construction
Add Binary ▼ expand
"""
# 67. Add Binary

Given two binary strings `a` and `b`, return their sum as a binary string.

## Examples

```text
Input: a = "11", b = "1"
Output: "100"

Input: a = "1010", b = "1011"
Output: "10101"
```

## Constraints
- 1 <= a.length, b.length <= 10^4
- a and b consist only of '0' or '1' characters.
- Each string does not contain leading zeros except for the zero itself.
"""


class Solution:
    def addBinary(self, a: str, b: str) -> str:
        # Simulate binary addition from right to left with carry
        # O(max(m,n)) time — iterate from end with carry, build result in reverse
        i, j, carry = len(a) - 1, len(b) - 1, 0
        result = []
        while i >= 0 or j >= 0 or carry:
            total = carry
            if i >= 0:
                total += ord(a[i]) - 48
                i -= 1
            if j >= 0:
                total += ord(b[j]) - 48
                j -= 1
            result.append(str(total % 2))
            carry = total // 2
        return ''.join(reversed(result))


if __name__ == '__main__':
    s = Solution()
    assert s.addBinary("11", "1") == "100"
    assert s.addBinary("1010", "1011") == "10101"
    assert s.addBinary("0", "0") == "0"
    print("All tests passed.")
Bitwise And Of Numbers Range ▼ expand
"""
# 201. Bitwise AND of Numbers Range

Given two integers `left` and `right` that represent the range `[left, right]`,
return the bitwise AND of all numbers in this range, inclusive.

## Examples

```text
Input: left = 5, right = 7
Output: 4

Input: left = 0, right = 0
Output: 0

Input: left = 1, right = 2147483647
Output: 0
```

## Constraints
- 0 <= left <= right <= 2^31 - 1
"""


class Solution:
    def rangeBitwiseAnd_shift(self, left: int, right: int) -> int:
        # O(32) time — right-shift both until equal to find common prefix
        shift = 0
        while left != right:
            left >>= 1
            right >>= 1
            shift += 1
        return left << shift

    def rangeBitwiseAnd(self, left: int, right: int) -> int:
        # Right-shift both until equal: common prefix of all numbers in range
        # Bits that differ across the range will be 0 in the AND result
        # O(32) time — Brian Kernighan: strip lowest set bit of right until right <= left
        while right > left:
            right &= right - 1
        return right


if __name__ == '__main__':
    s = Solution()
    for method in (s.rangeBitwiseAnd_shift, s.rangeBitwiseAnd):
        assert method(5, 7) == 4
        assert method(0, 0) == 0
        assert method(1, 2147483647) == 0
    print("All tests passed.")
Counting Bits ▼ expand
"""
# 338. Counting Bits

Given an integer `n`, return an array `ans` of length `n + 1` such that for each `i`
(0 <= i <= n), `ans[i]` is the number of 1's in the binary representation of `i`.

## Examples

```text
Input: n = 2
Output: [0,1,1]

Input: n = 5
Output: [0,1,1,2,1,2]
```

## Constraints
- 0 <= n <= 10^5
"""

from typing import List


class Solution:
    def countBits_naive(self, n: int) -> List[int]:
        # O(n * 32) time — count bits for each number independently
        def count(x: int) -> int:
            c = 0
            while x:
                x &= x - 1
                c += 1
            return c
        return [count(i) for i in range(n + 1)]

    def countBits(self, n: int) -> List[int]:
        # dp[i] = dp[i >> 1] + (i & 1): right shift removes last bit, add it back
        # O(n) time — dp: bits[i] = bits[i >> 1] + (i & 1)
        dp = [0] * (n + 1)
        for i in range(1, n + 1):
            dp[i] = dp[i >> 1] + (i & 1)
        return dp


if __name__ == '__main__':
    s = Solution()
    for method in (s.countBits_naive, s.countBits):
        assert method(2) == [0, 1, 1]
        assert method(5) == [0, 1, 1, 2, 1, 2]
    print("All tests passed.")
Minimum Array End ▼ expand
"""
# 3133. Minimum Array End

You are given two integers `n` and `x`. You have to construct an array of **positive** integers
`nums` of size `n` where for every `0 <= i < n - 1`, `nums[i + 1]` is greater than `nums[i]`,
and the result of the bitwise AND of all elements of `nums` is `x`.

Return the **minimum** possible value of `nums[n - 1]`.

## Examples

```text
Input: n = 3, x = 4
Output: 6
Explanation: nums = [4, 5, 6], AND = 4 & 5 & 6 = 4.

Input: n = 2, x = 7
Output: 15
Explanation: nums = [7, 15], AND = 7 & 15 = 7.
```

## Constraints
- 1 <= n, x <= 10^8
"""


class Solution:
    def minEnd(self, n: int, x: int) -> int:
        # Fill the 0-bits of x with bits of (n-1) in order
        # This gives the smallest number >= x that ANDs with x to give x
        # O(64) time — embed (n-1) into the zero-bits of x to get minimum last element
        result = x
        remaining = n - 1
        bit = 1
        while remaining:
            if not (result & bit):          # bit position is free (0 in x)
                if remaining & 1:
                    result |= bit
                remaining >>= 1
            bit <<= 1
        return result


if __name__ == '__main__':
    s = Solution()
    assert s.minEnd(3, 4) == 6
    assert s.minEnd(2, 7) == 15
    assert s.minEnd(1, 1) == 1
    print("All tests passed.")
Missing Number ▼ expand
"""
# 268. Missing Number

Given an array `nums` containing `n` distinct numbers in the range `[0, n]`,
return the only number in the range that is missing from the array.

## Examples

```text
Input: nums = [3,0,1]
Output: 2

Input: nums = [0,1]
Output: 2

Input: nums = [9,6,4,2,3,5,7,0,1]
Output: 8
```

## Constraints
- n == nums.length
- 1 <= n <= 10^4
- 0 <= nums[i] <= n
- All the numbers of nums are unique.
"""

from typing import List


class Solution:
    def missingNumber_sum(self, nums: List[int]) -> int:
        # O(n) time, O(1) space — expected sum minus actual sum
        n = len(nums)
        return n * (n + 1) // 2 - sum(nums)

    def missingNumber(self, nums: List[int]) -> int:
        # XOR all indices 0..n with all values; pairs cancel, leaving the missing number
        # O(n) time, O(1) space — XOR all indices and values; pairs cancel out
        result = len(nums)
        for i, n in enumerate(nums):
            result ^= i ^ n
        return result


if __name__ == '__main__':
    s = Solution()
    for method in (s.missingNumber_sum, s.missingNumber):
        assert method([3, 0, 1]) == 2
        assert method([0, 1]) == 2
        assert method([9, 6, 4, 2, 3, 5, 7, 0, 1]) == 8
    print("All tests passed.")
Number Of 1 Bits ▼ expand
"""
# 191. Number of 1 Bits

Given a positive integer `n`, write a function that returns the number of set bits
in its binary representation (also known as the **Hamming weight**).

## Examples

```text
Input: n = 11
Output: 3
Explanation: 11 in binary is 1011, which has 3 set bits.

Input: n = 128
Output: 1
Explanation: 128 in binary is 10000000, which has 1 set bit.

Input: n = 2147483645
Output: 30
```

## Constraints
- 1 <= n <= 2^31 - 1
"""


class Solution:
    def hammingWeight_each_bit(self, n: int) -> int:
        # O(32) time — check each of the 32 bits with a mask
        count = 0
        for _ in range(32):
            count += n & 1
            n >>= 1
        return count

    def hammingWeight(self, n: int) -> int:
        # n & (n-1) clears the lowest set bit; count how many times until n=0
        # O(k) time where k = number of set bits — n & (n-1) clears lowest set bit
        count = 0
        while n:
            n &= n - 1
            count += 1
        return count


if __name__ == '__main__':
    s = Solution()
    for method in (s.hammingWeight_each_bit, s.hammingWeight):
        assert method(11) == 3
        assert method(128) == 1
        assert method(1) == 1
    print("All tests passed.")
Reverse Bits ▼ expand
"""
# 190. Reverse Bits

Reverse bits of a given 32 bits unsigned integer.

## Examples

```text
Input: n = 00000010100101000001111010011100
Output:    964176192 (00111001011110000010100101000000)

Input: n = 11111111111111111111111111111101
Output:    3221225471 (10111111111111111111111111111111)
```

## Constraints
- The input must be a binary string of length 32.
"""


class Solution:
    def reverseBits_bitbybit(self, n: int) -> int:
        # O(32) time — shift bits one at a time into result
        result = 0
        for _ in range(32):
            result = (result << 1) | (n & 1)
            n >>= 1
        return result

    def reverseBits(self, n: int) -> int:
        # For each of 32 bits: shift result left, OR in the lowest bit of n, shift n right
        # O(1) time — divide and conquer: swap 16, 8, 4, 2, 1-bit groups
        n = (n >> 16) | (n << 16)
        n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8)
        n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4)
        n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2)
        n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1)
        return n & 0xFFFFFFFF


if __name__ == '__main__':
    s = Solution()
    for method in (s.reverseBits_bitbybit, s.reverseBits):
        assert method(43261596) == 964176192
        assert method(0) == 0
        assert method(0xFFFFFFFF) == 0xFFFFFFFF
    print("All tests passed.")
Reverse Integer ▼ expand
"""
# 7. Reverse Integer

Given a signed 32-bit integer `x`, return `x` with its digits reversed.
If reversing `x` causes the value to go outside the signed 32-bit integer range [-2^31, 2^31 - 1],
return 0.

**Assume the environment does not allow you to store 64-bit integers (signed or unsigned).**

## Examples

```text
Input: x = 123
Output: 321

Input: x = -123
Output: -321

Input: x = 120
Output: 21
```

## Constraints
- -2^31 <= x <= 2^31 - 1
"""

INT_MIN, INT_MAX = -(2**31), 2**31 - 1


class Solution:
    def reverse_string(self, x: int) -> int:
        # O(log n) time — convert to string, reverse, check overflow
        sign = -1 if x < 0 else 1
        rev = int(str(abs(x))[::-1]) * sign
        return rev if INT_MIN <= rev <= INT_MAX else 0

    def reverse(self, x: int) -> int:
        # Extract digits via mod 10, build reversed number; check 32-bit overflow
        # O(log n) time — extract digits with modulo, check overflow before each step
        sign = -1 if x < 0 else 1
        x = abs(x)
        result = 0
        while x:
            x, digit = divmod(x, 10)
            result = result * 10 + digit
        result *= sign
        return result if INT_MIN <= result <= INT_MAX else 0


if __name__ == '__main__':
    s = Solution()
    for method in (s.reverse_string, s.reverse):
        assert method(123) == 321
        assert method(-123) == -321
        assert method(120) == 21
        assert method(1534236469) == 0  # overflow
    print("All tests passed.")
Single Number ▼ expand
"""
# 136. Single Number

Given a non-empty array of integers `nums`, every element appears **twice** except for one.
Find that single one.

You must implement a solution with linear runtime complexity and use only constant extra space.

## Examples

```text
Input: nums = [2,2,1]
Output: 1

Input: nums = [4,1,2,1,2]
Output: 4

Input: nums = [1]
Output: 1
```

## Constraints
- 1 <= nums.length <= 3 * 10^4
- -3 * 10^4 <= nums[i] <= 3 * 10^4
- Each element in the array appears twice except for one element which appears only once.
"""

from typing import List


class Solution:
    def singleNumber_set(self, nums: List[int]) -> int:
        # O(n) time, O(n) space — hash set: add if unseen, remove if seen
        seen = set()
        for n in nums:
            if n in seen:
                seen.remove(n)
            else:
                seen.add(n)
        return seen.pop()

    def singleNumber(self, nums: List[int]) -> int:
        # XOR all numbers: pairs cancel out (a^a=0), leaving the single number
        # O(n) time, O(1) space — XOR cancels pairs, leaving the single element
        result = 0
        for n in nums:
            result ^= n
        return result


if __name__ == '__main__':
    s = Solution()
    for method in (s.singleNumber_set, s.singleNumber):
        assert method([2, 2, 1]) == 1
        assert method([4, 1, 2, 1, 2]) == 4
        assert method([1]) == 1
    print("All tests passed.")
Sum Of Two Integers ▼ expand
"""
# 371. Sum of Two Integers

Given two integers `a` and `b`, return the sum of the two integers without using
the operators `+` and `-`.

## Examples

```text
Input: a = 1, b = 2
Output: 3

Input: a = 2, b = 3
Output: 5
```

## Constraints
- -1000 <= a, b <= 1000
"""


class Solution:
    def getSum(self, a: int, b: int) -> int:
        # XOR gives sum without carry; AND<<1 gives the carry
        # Repeat until no carry remains; mask to 32 bits to handle negatives
        # O(32) time — XOR gives sum without carry; AND<<1 gives carry; repeat until no carry
        mask = 0xFFFFFFFF
        while b & mask:
            carry = (a & b) << 1
            a = a ^ b
            b = carry
        # handle negative numbers in Python's arbitrary precision integers
        return a if b == 0 else a & mask if (a & (mask + 1)) else a


if __name__ == '__main__':
    s = Solution()
    assert s.getSum(1, 2) == 3
    assert s.getSum(2, 3) == 5
    assert s.getSum(-1, 1) == 0
    assert s.getSum(-5, 3) == -2
    print("All tests passed.")

Bit Manipulation

Visualize bitwise operations on 8-bit values

bit pos
7
6
5
4
3
2
1
0
A
0
0
1
0
1
0
1
0
= 42
B
0
0
0
1
1
0
1
1
= 27
A & B
0
0
0
0
1
0
1
0
= 10