Bit Manipulation

Work on the binary form of integers directly: test and flip single bits, cancel pairs with XOR, and pack a small set into one number.

IntermediateRead first: Big-O & Complexity

How It Works

Every integer is stored as a row of bits, and the bitwise operators work on all of those bits at once in a single machine instruction. Some problems that look like they need a hash set or a loop over every value collapse into a few O(1) operations once you think in bits.

a      = 01100   12
b      = 01010   10
a & b  = 01000    8   1 where both bits are 1
a | b  = 01110   14   1 where either bit is 1
a ^ b  = 00110    6   1 where the bits differ
a << 1 = 11000   24   shift left: times 2
a >> 2 = 00011    3   shift right: divide by 4, round down
Bit positions count from 0 at the right, so bit 2 of 12 is the 4s place.

A handful of identities do most of the work. With mask = 1 << i you can read bit i ((x >> i) & 1), set it (x | mask), clear it (x & ~mask) or flip it (x ^ mask). x & (x - 1) clears the lowest set bit, and x & -x keeps only that bit. ~x flips every bit, which in two's complement equals -x - 1.

Clearing the Lowest Set Bit

Step 1 of 6

  • Lowest set bit
  • Bits that changed
  • Answer

x = 12 is 01100 in binary. popCount clears the lowest set bit until x is 0, so it loops once per set bit, not once per bit position.

All 6 steps as text
  1. x = 12 is 01100 in binary. popCount clears the lowest set bit until x is 0, so it loops once per set bit, not once per bit position.
  2. x - 1 = 11 borrows from the lowest set bit, bit 2. It turns off, the 0s below it turn on, and every bit above stays the same.
  3. AND keeps a bit only where both rows have a 1. Bits 2 to 0 differ, so the lowest set bit is gone: 12 & 11 = 8. Count the pass: count = 1.
  4. Next pass, with x = 8. x - 1 = 7 borrows from the lowest set bit, bit 3. It turns off, the 0s below it turn on, and every bit above stays the same.
  5. AND keeps a bit only where both rows have a 1. Bits 3 to 0 differ, so the lowest set bit is gone: 8 & 7 = 0. Count the pass: count = 2.
  6. x is 0, so the loop stops after 2 passes, one per set bit. popCount(12) = 2.

XOR deserves special attention. a ^ a = 0, a ^ 0 = a, and the order you apply it in doesn't matter. So XOR-ing a list together cancels every value that appears an even number of times. That's the whole trick behind Single Number below, and behind the XOR version of Missing Number in Big-O. Most bit problems take one of four shapes:

  • Cancel pairs with XOR. Find the value that appears once, or the one that's missing from a range.
  • Count and test bits. Popcount, power-of-two checks and the number of positions where two values differ.
  • Bitmasks as sets. An n-bit integer can stand for a subset of n items: bit i is set when item i is in. Counting mask from 0 to 2^n - 1 visits every subset with a plain loop instead of backtracking, and bitmask DP uses the same encoding to name its states.
  • Build on smaller numbers. i >> 1 is i with its lowest bit dropped, so a table can reuse the answer for i >> 1 when it fills in i. That's Counting Bits below.

When to Reach for It

  • The statement mentions binary, bits, XOR or powers of two.
  • Every value appears twice except one, and the question wants O(1) extra space.
  • You need to represent a subset of a small set, about 20 items or fewer, or enumerate all of its subsets.
  • The problem forbids +, -, * or / and expects you to build arithmetic out of bitwise operators.

Complexity

OperationTimeSpace
Get, set, clear or toggle one bitO(1)O(1)
Popcount by clearing the lowest set bitO(k) for k set bitsO(1)
XOR a whole array togetherO(n)O(1)
Check every subset of n items as a bitmaskO(n · 2^n)O(1) beyond the output

The Template

Everyday Bit Tricks

getBit shifts x right by i and keeps the last bit. setBit, clearBit and toggleBit build a one-bit mask with 1 << i and combine it with x: OR sets the bit, AND with the inverted mask clears it, and XOR flips it. Go has a single operator for AND NOT, &^, so its clearBit reads a little differently. None of these loop, so each is O(1).

popCount uses Brian Kernighan's trick: x & (x - 1) clears the lowest set bit, so the loop runs once per set bit instead of once per bit position. isPowerOfTwo uses the same expression, because a power of two has exactly one set bit and clearing it leaves 0. lowestSetBit uses x & -x. In two's complement -x is ~x + 1, and adding that 1 carries up through the trailing ones of ~x until it lands on the lowest set bit of x. That bit is the only one x and -x have in common.

Time O(1), and popCount loops once per set bitSpace O(1)

def get_bit(x: int, i: int) -> int:
    """Bit i of x, as 0 or 1. Bits count from 0 at the right."""
    return (x >> i) & 1


def set_bit(x: int, i: int) -> int:
    """x with bit i turned on."""
    return x | (1 << i)


def clear_bit(x: int, i: int) -> int:
    """x with bit i turned off."""
    return x & ~(1 << i)


def toggle_bit(x: int, i: int) -> int:
    """x with bit i flipped."""
    return x ^ (1 << i)


def pop_count(x: int) -> int:
    """How many bits of x (x >= 0) are set. Each pass clears the lowest one."""
    count = 0
    while x != 0:
        x &= x - 1
        count += 1
    return count


def lowest_set_bit(x: int) -> int:
    """Only the lowest set bit of x: 12 (1100) gives 4 (0100)."""
    return x & -x


def is_power_of_two(x: int) -> bool:
    """A power of two has exactly one set bit, so clearing it leaves 0."""
    return x > 0 and (x & (x - 1)) == 0


if __name__ == "__main__":
    x = 12  # 1100 in binary
    print(get_bit(x, 2), get_bit(x, 0))  # 1 0
    print(set_bit(x, 0), clear_bit(x, 3), toggle_bit(x, 1))  # 13 4 14
    # Each line: n, pop_count, lowest_set_bit, is_power_of_two.
    for n in [12, 16, 7, 0]:
        print(n, pop_count(n), lowest_set_bit(n), is_power_of_two(n))
// Bit i of x, as 0 or 1. Bits count from 0 at the right.
export function getBit(x: number, i: number): number {
  return (x >> i) & 1;
}

// x with bit i turned on.
export function setBit(x: number, i: number): number {
  return x | (1 << i);
}

// x with bit i turned off.
export function clearBit(x: number, i: number): number {
  return x & ~(1 << i);
}

// x with bit i flipped.
export function toggleBit(x: number, i: number): number {
  return x ^ (1 << i);
}

// How many bits of x (x >= 0) are set. Each pass clears the lowest one.
export function popCount(x: number): number {
  let count = 0;
  while (x !== 0) {
    x &= x - 1;
    count++;
  }
  return count;
}

// Only the lowest set bit of x: 12 (1100) gives 4 (0100).
export function lowestSetBit(x: number): number {
  return x & -x;
}

// A power of two has exactly one set bit, so clearing it leaves 0.
export function isPowerOfTwo(x: number): boolean {
  return x > 0 && (x & (x - 1)) === 0;
}

const x = 12; // 1100 in binary
console.log(getBit(x, 2), getBit(x, 0)); // 1 0
console.log(setBit(x, 0), clearBit(x, 3), toggleBit(x, 1)); // 13 4 14
// Each line: n, popCount, lowestSetBit, isPowerOfTwo.
for (const n of [12, 16, 7, 0]) {
  console.log(n, popCount(n), lowestSetBit(n), isPowerOfTwo(n));
}
package main

import "fmt"

// getBit returns bit i of x, as 0 or 1. Bits count from 0 at the right.
func getBit(x, i int) int {
	return (x >> i) & 1
}

// setBit returns x with bit i turned on.
func setBit(x, i int) int {
	return x | (1 << i)
}

// clearBit returns x with bit i turned off. Go spells AND NOT as &^.
func clearBit(x, i int) int {
	return x &^ (1 << i)
}

// toggleBit returns x with bit i flipped.
func toggleBit(x, i int) int {
	return x ^ (1 << i)
}

// popCount returns how many bits of x (x >= 0) are set. Each pass clears the
// lowest one.
func popCount(x int) int {
	count := 0
	for x != 0 {
		x &= x - 1
		count++
	}
	return count
}

// lowestSetBit returns only the lowest set bit of x: 12 (1100) gives 4 (0100).
func lowestSetBit(x int) int {
	return x & -x
}

// isPowerOfTwo reports whether x is a power of two. A power of two has exactly
// one set bit, so clearing it leaves 0.
func isPowerOfTwo(x int) bool {
	return x > 0 && x&(x-1) == 0
}

func main() {
	x := 12 // 1100 in binary

	fmt.Println(getBit(x, 2), getBit(x, 0))                    // 1 0
	fmt.Println(setBit(x, 0), clearBit(x, 3), toggleBit(x, 1)) // 13 4 14
	// Each line: n, popCount, lowestSetBit, isPowerOfTwo.
	for _, n := range []int{12, 16, 7, 0} {
		fmt.Println(n, popCount(n), lowestSetBit(n), isPowerOfTwo(n))
	}
}
using System;

public class Solution
{
    // Bit i of x, as 0 or 1. Bits count from 0 at the right.
    public static int GetBit(int x, int i) => (x >> i) & 1;

    // x with bit i turned on.
    public static int SetBit(int x, int i) => x | (1 << i);

    // x with bit i turned off.
    public static int ClearBit(int x, int i) => x & ~(1 << i);

    // x with bit i flipped.
    public static int ToggleBit(int x, int i) => x ^ (1 << i);

    // How many bits of x (x >= 0) are set. Each pass clears the lowest one.
    public static int PopCount(int x)
    {
        int count = 0;
        while (x != 0)
        {
            x &= x - 1;
            count++;
        }
        return count;
    }

    // Only the lowest set bit of x: 12 (1100) gives 4 (0100).
    public static int LowestSetBit(int x) => x & -x;

    // A power of two has exactly one set bit, so clearing it leaves 0.
    public static bool IsPowerOfTwo(int x) => x > 0 && (x & (x - 1)) == 0;

    public static void Main()
    {
        int x = 12; // 1100 in binary
        Console.WriteLine($"{GetBit(x, 2)} {GetBit(x, 0)}"); // 1 0
        Console.WriteLine($"{SetBit(x, 0)} {ClearBit(x, 3)} {ToggleBit(x, 1)}"); // 13 4 14
        // Each line: n, PopCount, LowestSetBit, IsPowerOfTwo.
        foreach (int n in new[] { 12, 16, 7, 0 })
        {
            Console.WriteLine($"{n} {PopCount(n)} {LowestSetBit(n)} {IsPowerOfTwo(n)}");
        }
    }
}
public class Solution {
    // Bit i of x, as 0 or 1. Bits count from 0 at the right.
    static int getBit(int x, int i) {
        return (x >> i) & 1;
    }

    // x with bit i turned on.
    static int setBit(int x, int i) {
        return x | (1 << i);
    }

    // x with bit i turned off.
    static int clearBit(int x, int i) {
        return x & ~(1 << i);
    }

    // x with bit i flipped.
    static int toggleBit(int x, int i) {
        return x ^ (1 << i);
    }

    // How many bits of x (x >= 0) are set. Each pass clears the lowest one.
    static int popCount(int x) {
        int count = 0;
        while (x != 0) {
            x &= x - 1;
            count++;
        }
        return count;
    }

    // Only the lowest set bit of x: 12 (1100) gives 4 (0100).
    static int lowestSetBit(int x) {
        return x & -x;
    }

    // A power of two has exactly one set bit, so clearing it leaves 0.
    static boolean isPowerOfTwo(int x) {
        return x > 0 && (x & (x - 1)) == 0;
    }

    public static void main(String[] args) {
        int x = 12; // 1100 in binary
        System.out.println(getBit(x, 2) + " " + getBit(x, 0)); // 1 0
        System.out.println(setBit(x, 0) + " " + clearBit(x, 3) + " " + toggleBit(x, 1)); // 13 4 14
        // Each line: n, popCount, lowestSetBit, isPowerOfTwo.
        for (int n : new int[] {12, 16, 7, 0}) {
            System.out.println(n + " " + popCount(n) + " " + lowestSetBit(n) + " " + isPowerOfTwo(n));
        }
    }
}

Worked Problems

Each solution stays folded until you open it, so you can try the problem first. Every one is a complete program in Python, TypeScript, Go, C# and Java, run and checked against its expected output.

Single Number

EASYLeetCode 136

Problem

In a non-empty array nums, every value appears exactly twice except for one value that appears once. Find that value in O(n) time using only O(1) extra space.

Example: [4, 1, 2, 1, 2] returns 4, the only value without a partner.

Show Solution

Approach

A hash set would find it in O(n) time, but it needs O(n) space. XOR every value into one accumulator instead. Each pair cancels to 0 because a ^ a = 0, the order doesn't matter, and 0 ^ single = single.

  4 ^ 1 ^ 2 ^ 1 ^ 2
= 4 ^ (1 ^ 1) ^ (2 ^ 2)   regroup: order doesn't matter
= 4 ^ 0 ^ 0               each pair cancels
= 4

Single Number, Step by Step

Step 1 of 7

  • 1 bits being XOR-ed in
  • Bits that flipped
  • Already folded in
  • Answer

Start with result = 0. XOR-ing a value in flips each bit of result where that value has a 1, and leaves the other bits alone.

All 7 steps as text
  1. Start with result = 0. XOR-ing a value in flips each bit of result where that value has a 1, and leaves the other bits alone.
  2. 0 ^ 4 = 4: XOR-ing 4 in turns bit 2 on. 4 is new, so nothing cancels it yet.
  3. 4 ^ 1 = 5: XOR-ing 1 in turns bit 0 on. 1 is new, so nothing cancels it yet.
  4. 5 ^ 2 = 7: XOR-ing 2 in turns bit 1 on. 2 is new, so nothing cancels it yet.
  5. 7 ^ 1 = 6: XOR-ing 1 in turns bit 0 off. 1 showed up before, so its pair cancels.
  6. 6 ^ 2 = 4: XOR-ing 2 in turns bit 1 off. 2 showed up before, so its pair cancels.
  7. Each pair flipped the same bits twice, which undoes both flips, so only the single value is left. singleNumber returns 4.

Negative numbers work too, since XOR acts on the two's complement bits directly. The trick depends on every other value appearing an even number of times. If they appear three times, XOR no longer cancels them. Count each bit position modulo 3 instead, as in Single Number II from the practice list.

Time O(n)Space O(1)

def single_number(nums: list[int]) -> int:
    """The value that appears once in `nums`, when every other value appears twice."""
    result = 0
    # Pairs cancel (a ^ a = 0) and order doesn't matter, so only the single
    # value is left (0 ^ single = single).
    for num in nums:
        result ^= num
    return result


if __name__ == "__main__":
    print(single_number([2, 2, 1]))  # 1
    print(single_number([4, 1, 2, 1, 2]))  # 4
    print(single_number([1]))  # 1
    print(single_number([7, -3, 7]))  # -3
// The value that appears once in nums, when every other value appears twice.
export function singleNumber(nums: number[]): number {
  let result = 0;
  // Pairs cancel (a ^ a = 0) and order doesn't matter, so only the single
  // value is left (0 ^ single = single).
  for (const num of nums) result ^= num;
  return result;
}

console.log(singleNumber([2, 2, 1])); // 1
console.log(singleNumber([4, 1, 2, 1, 2])); // 4
console.log(singleNumber([1])); // 1
console.log(singleNumber([7, -3, 7])); // -3
package main

import "fmt"

// singleNumber returns the value that appears once in nums, when every other
// value appears twice.
func singleNumber(nums []int) int {
	result := 0
	// Pairs cancel (a ^ a = 0) and order doesn't matter, so only the single
	// value is left (0 ^ single = single).
	for _, num := range nums {
		result ^= num
	}
	return result
}

func main() {
	fmt.Println(singleNumber([]int{2, 2, 1}))       // 1
	fmt.Println(singleNumber([]int{4, 1, 2, 1, 2})) // 4
	fmt.Println(singleNumber([]int{1}))             // 1
	fmt.Println(singleNumber([]int{7, -3, 7}))      // -3
}
using System;

public class Solution
{
    // The value that appears once in nums, when every other value appears twice.
    public static int SingleNumber(int[] nums)
    {
        int result = 0;
        // Pairs cancel (a ^ a = 0) and order doesn't matter, so only the single
        // value is left (0 ^ single = single).
        foreach (int num in nums) result ^= num;
        return result;
    }

    public static void Main()
    {
        Console.WriteLine(SingleNumber(new[] { 2, 2, 1 })); // 1
        Console.WriteLine(SingleNumber(new[] { 4, 1, 2, 1, 2 })); // 4
        Console.WriteLine(SingleNumber(new[] { 1 })); // 1
        Console.WriteLine(SingleNumber(new[] { 7, -3, 7 })); // -3
    }
}
public class Solution {
    // The value that appears once in nums, when every other value appears twice.
    static int singleNumber(int[] nums) {
        int result = 0;
        // Pairs cancel (a ^ a = 0) and order doesn't matter, so only the single
        // value is left (0 ^ single = single).
        for (int num : nums) result ^= num;
        return result;
    }

    public static void main(String[] args) {
        System.out.println(singleNumber(new int[] {2, 2, 1})); // 1
        System.out.println(singleNumber(new int[] {4, 1, 2, 1, 2})); // 4
        System.out.println(singleNumber(new int[] {1})); // 1
        System.out.println(singleNumber(new int[] {7, -3, 7})); // -3
    }
}

Counting Bits

EASYLeetCode 338

Problem

Given an integer n, return an array ans of length n + 1 where ans[i] is the number of 1 bits in the binary form of i.

Example: n = 5 returns [0, 1, 1, 2, 1, 2], because 0 to 5 are 0, 1, 10, 11, 100 and 101 in binary.

Show Solution

Approach

Running popCount on every number works, but each number up to n has as many as log n set bits, so that's O(n log n) in the worst case. You can do it in O(n) by reusing earlier answers. i >> 1 is i with its lowest bit removed. It's smaller than i, so its count is already in the table, and you only add back the bit you removed, i & 1.

i   binary   i >> 1   ans[i >> 1] + (i & 1)
5   101      2 (10)   1 + 1 = 2
6   110      3 (11)   2 + 0 = 2
7   111      3 (11)   2 + 1 = 3

Each entry costs O(1), so the whole table is O(n). This is a small taste of dynamic programming: the answer for i is built from a stored answer for a smaller input.

Time O(n)Space O(1) beyond the output

def count_bits(n: int) -> list[int]:
    """ans[i] is the number of set bits in i, for every i from 0 to n."""
    ans = [0] * (n + 1)
    for i in range(1, n + 1):
        # i >> 1 is i without its lowest bit. It's smaller than i, so its count
        # is already in the table. Add back the bit that was dropped.
        ans[i] = ans[i >> 1] + (i & 1)
    return ans


if __name__ == "__main__":
    print(count_bits(2))  # [0, 1, 1]
    print(count_bits(5))  # [0, 1, 1, 2, 1, 2]
    print(count_bits(0))  # [0]
// ans[i] is the number of set bits in i, for every i from 0 to n.
export function countBits(n: number): number[] {
  const ans = new Array<number>(n + 1).fill(0);
  for (let i = 1; i <= n; i++) {
    // i >> 1 is i without its lowest bit. It's smaller than i, so its count
    // is already in the table. Add back the bit that was dropped.
    ans[i] = ans[i >> 1] + (i & 1);
  }
  return ans;
}

console.log(JSON.stringify(countBits(2))); // [0,1,1]
console.log(JSON.stringify(countBits(5))); // [0,1,1,2,1,2]
console.log(JSON.stringify(countBits(0))); // [0]
package main

import "fmt"

// countBits returns ans, where ans[i] is the number of set bits in i, for
// every i from 0 to n.
func countBits(n int) []int {
	ans := make([]int, n+1)
	for i := 1; i <= n; i++ {
		// i >> 1 is i without its lowest bit. It's smaller than i, so its count
		// is already in the table. Add back the bit that was dropped.
		ans[i] = ans[i>>1] + (i & 1)
	}
	return ans
}

func main() {
	fmt.Println(countBits(2)) // [0 1 1]
	fmt.Println(countBits(5)) // [0 1 1 2 1 2]
	fmt.Println(countBits(0)) // [0]
}
using System;

public class Solution
{
    // ans[i] is the number of set bits in i, for every i from 0 to n.
    public static int[] CountBits(int n)
    {
        var ans = new int[n + 1];
        for (int i = 1; i <= n; i++)
        {
            // i >> 1 is i without its lowest bit. It's smaller than i, so its count
            // is already in the table. Add back the bit that was dropped.
            ans[i] = ans[i >> 1] + (i & 1);
        }
        return ans;
    }

    public static void Main()
    {
        Console.WriteLine(Format(CountBits(2))); // [0, 1, 1]
        Console.WriteLine(Format(CountBits(5))); // [0, 1, 1, 2, 1, 2]
        Console.WriteLine(Format(CountBits(0))); // [0]
    }

    static string Format(int[] values) => "[" + string.Join(", ", values) + "]";
}
import java.util.Arrays;

public class Solution {
    // ans[i] is the number of set bits in i, for every i from 0 to n.
    static int[] countBits(int n) {
        int[] ans = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            // i >> 1 is i without its lowest bit. It's smaller than i, so its count
            // is already in the table. Add back the bit that was dropped.
            ans[i] = ans[i >> 1] + (i & 1);
        }
        return ans;
    }

    public static void main(String[] args) {
        System.out.println(Arrays.toString(countBits(2))); // [0, 1, 1]
        System.out.println(Arrays.toString(countBits(5))); // [0, 1, 1, 2, 1, 2]
        System.out.println(Arrays.toString(countBits(0))); // [0]
    }
}

Common Pitfalls

  • Operator precedence. In JavaScript, Java and C#, == binds tighter than &, so x & 1 == 0 doesn't mean (x & 1) == 0. JavaScript quietly computes the wrong thing, and Java and C# refuse to compile it. Wrap bit expressions in parentheses.
  • Calling 0 a power of two. (x & (x - 1)) == 0 is true for x = 0, so check x > 0 first.
  • Shifting a negative number right. >> copies the sign bit, so -8 >> 1 is -4 and a loop that shifts until it reaches 0 gets stuck at -1 and never ends. Use >>> in Java and JavaScript, an unsigned type in Go and C#, or mask with & 0xFFFFFFFF in Python, where integers have no fixed width.
  • Shifting by 32 or more on a 32-bit int. JavaScript, Java and C# only use the low 5 bits of the shift count, so 1 << 32 is 1. Use a 64-bit type, such as 1L << 40 in Java, when you need higher bits.
  • Forgetting that JavaScript's bitwise operators work on 32-bit signed integers. 2 ** 31 | 0 is negative, and any bits above 32 are silently dropped. Use BigInt for wider values.

Practice

More problems to try on your own.