Data Structures & Algorithms Cheatsheet
Bit Manipulation
Use this Data Structures & Algorithms reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Operators
| Operator | Name | Example | Result |
|---|---|---|---|
& | AND | 0b1100 & 0b1010 | 0b1000 |
| | OR | 0b1100 | 0b1010 | 0b1110 |
^ | XOR | 0b1100 ^ 0b1010 | 0b0110 |
~ | NOT | ~5 | -6 (two's complement) |
<< | left shift | 5 << 2 | 20, times 2^k |
>> | arithmetic right shift | -8 >> 1 | -4, sign-extending |
>>> | logical right shift (Java) | -8 >>> 28 | 15, zero-filled |
Precedence in C, C++, Java, and Python puts comparison above the bitwise operators, so a & 1 == 0 parses as a & (1 == 0). Always parenthesize: (a & 1) == 0.
Core Identities
| Identity | Meaning |
|---|---|
x ^ x = 0 | XOR is self-cancelling |
x ^ 0 = x | 0 is the XOR identity |
x ^ y ^ y = x | XOR is its own inverse, the basis of the "single number" trick |
x & (x - 1) | clears the lowest set bit |
x & -x | isolates the lowest set bit |
x | (x + 1) | sets the lowest zero bit |
x & (x - 1) == 0 | x is a power of two (guard x > 0) |
x ^ (x >> 1) | binary to Gray code |
a + b = (a ^ b) + 2 * (a & b) | XOR is sum without carry, AND is the carry |
a + b = (a | b) + (a & b) | another decomposition |
~x = -x - 1 | two's complement relation |
x >> k | floor divide by 2^k, not truncating divide for negatives |
Single-Bit Operations
# test bit i (x >> i) & 1 x & (1 << i) # truthy, not necessarily 1 # set bit i x |= 1 << i # clear bit i x &= ~(1 << i) # toggle bit i x ^= 1 << i # set bit i to a given value v (0 or 1), branch free x = (x & ~(1 << i)) | (v << i) # extract the low k bits x & ((1 << k) - 1) # clear all bits above position i (inclusive) x &= (1 << i) - 1 # clear all bits below position i (inclusive) x &= ~((1 << (i + 1)) - 1)
(1 << k) - 1 is the k-bit mask of all ones, and it appears in almost every bitmask solution.
Counting And Scanning Bits
x.bit_count() # Python 3.10+, popcount bin(x).count("1") # older Python x.bit_length() # position of the highest set bit, 0 for x == 0 x & -x # lowest set bit as a value (x & -x).bit_length() - 1 # index of the lowest set bit def popcount_kernighan(x): """Loops once per SET bit, not once per bit.""" count = 0 while x: x &= x - 1 count += 1 return count def iterate_set_bits(x): """Yield the index of every set bit, cheaply.""" while x: low = x & -x yield low.bit_length() - 1 x ^= low
Integer.bitCount(x); // popcount Integer.numberOfTrailingZeros(x); // index of the lowest set bit, 32 if x == 0 Integer.numberOfLeadingZeros(x); Integer.highestOneBit(x); // the value, not the index Integer.reverse(x); Integer.toBinaryString(x); Long.bitCount(x);
__builtin_popcount(x); // and __builtin_popcountll for 64-bit __builtin_ctz(x); // count trailing zeros, UNDEFINED for x == 0 __builtin_clz(x); // count leading zeros, UNDEFINED for x == 0 std::popcount(x); // C++20, well defined std::bit_width(x); // C++20, 1 + floor(log2 x) std::has_single_bit(x); // C++20, power-of-two test
__builtin_ctz(0)and__builtin_clz(0)are undefined behavior, not 32. Guard the zero case or use the C++20 functions.
Powers Of Two And Rounding
def is_power_of_two(x): return x > 0 and (x & (x - 1)) == 0 def is_power_of_four(x): return x > 0 and (x & (x - 1)) == 0 and (x - 1) % 3 == 0 def next_power_of_two(x): """Smallest power of two >= x.""" if x <= 1: return 1 return 1 << (x - 1).bit_length() def prev_power_of_two(x): return 1 << (x.bit_length() - 1) if x else 0 def round_up_multiple_of_pow2(x, k): """Round x up to a multiple of 2^k.""" mask = (1 << k) - 1 return (x + mask) & ~mask def swap_no_temp(a, b): a ^= b b ^= a a ^= b return a, b
Arithmetic replacements, useful mainly for reading other people's code:
| Expression | Bit form | Caveat |
|---|---|---|
x * 2^k | x << k | overflow on fixed-width ints |
x // 2^k for x ≥ 0 | x >> k | floors, so it differs from C truncation on negatives |
x % 2^k for x ≥ 0 | x & ((1 << k) - 1) | wrong for negative x outside Python |
x % 2 == 0 | (x & 1) == 0 | works for negatives |
abs(x) (32-bit) | (x ^ (x >> 31)) - (x >> 31) | branch-free trick |
Modern compilers already do these. Write the arithmetic form for clarity unless you are in a measured hot loop.
XOR Problems
XOR's self-cancelling property solves a family of problems in O(n) time and O(1) space.
def single_number(nums): """Every element appears twice except one. XOR everything.""" out = 0 for x in nums: out ^= x return out def single_number_two_singles(nums): """Exactly two elements appear once. Split by a differing bit.""" xor_all = 0 for x in nums: xor_all ^= x low = xor_all & -xor_all # a bit where the two answers differ a = b = 0 for x in nums: if x & low: a ^= x else: b ^= x return [a, b] def single_number_three_times(nums): """Every element appears three times except one. Count bits mod 3.""" ones = twos = 0 for x in nums: ones = (ones ^ x) & ~twos twos = (twos ^ x) & ~ones return ones def missing_number(nums): """0..n with one missing. XOR indices against values.""" out = len(nums) for i, x in enumerate(nums): out ^= i ^ x return out def find_duplicate_and_missing(nums): """1..n where one value repeats and one is missing.""" xor_all = 0 for i, x in enumerate(nums, 1): xor_all ^= i ^ x low = xor_all & -xor_all a = b = 0 for x in list(nums) + list(range(1, len(nums) + 1)): if x & low: a ^= x else: b ^= x return (a, b) if nums.count(a) > 1 else (b, a) def xor_prefix_range(a, b): """XOR of all integers in [a, b] using a closed form for [0, n].""" def upto(n): return [n, 1, n + 1, 0][n % 4] return upto(b) ^ upto(a - 1)
XOR is also the basis of the Gray code, of finding a maximum XOR pair with a binary trie, and of Nim's winning condition (the XOR of the pile sizes).
Subset Enumeration And Bitmask DP
A bitmask is a set: bit i set means element i is in the set. That makes subsets iterable with a plain loop.
def all_subsets(items): """Enumerate all 2^n subsets. O(2^n * n).""" n = len(items) out = [] for mask in range(1 << n): out.append([items[i] for i in range(n) if (mask >> i) & 1]) return out def submasks(mask): """Every submask of mask, in decreasing order. Total work over all masks is O(3^n), not O(4^n).""" sub = mask while True: yield sub if sub == 0: break sub = (sub - 1) & mask def count_bits_dp(n): """Popcount for 0..n in O(n). dp[i] = dp[i >> 1] + (i & 1).""" dp = [0] * (n + 1) for i in range(1, n + 1): dp[i] = dp[i >> 1] + (i & 1) return dp def gray_code(n): """A sequence where consecutive entries differ by exactly one bit.""" return [i ^ (i >> 1) for i in range(1 << n)] def min_cost_assignment(cost): """n workers to n jobs, minimum total. Bitmask DP, O(2^n * n).""" n = len(cost) INF = float("inf") dp = [INF] * (1 << n) dp[0] = 0 for mask in range(1 << n): if dp[mask] == INF: continue i = bin(mask).count("1") # next worker to assign if i == n: continue for j in range(n): if not (mask >> j) & 1: nm = mask | (1 << j) dp[nm] = min(dp[nm], dp[mask] + cost[i][j]) return dp[(1 << n) - 1]
Common bitmask set operations:
| Set operation | Bit form |
|---|---|
| union | a | b |
| intersection | a & b |
| difference a minus b | a & ~b |
| symmetric difference | a ^ b |
| is b a subset of a | (a & b) == b |
| add element i | a | (1 << i) |
| remove element i | a & ~(1 << i) |
| size | a.bit_count() |
| full set of n elements | (1 << n) - 1 |
| complement within n bits | a ^ ((1 << n) - 1) |
Fixed-Width Arithmetic In Python
Python integers are arbitrary precision, so 32-bit problems need explicit masking.
MASK = 0xFFFFFFFF INT_MIN, INT_MAX = -(1 << 31), (1 << 31) - 1 def to_signed_32(x): """Interpret the low 32 bits as a signed value.""" x &= MASK return x - (1 << 32) if x >= 1 << 31 else x def add_without_plus(a, b): """XOR is the sum without carry, AND << 1 is the carry.""" while b: a, b = (a ^ b) & MASK, ((a & b) << 1) & MASK return to_signed_32(a) def reverse_bits_32(x): out = 0 for _ in range(32): out = (out << 1) | (x & 1) x >>= 1 return out
// Java: >>> is the logical shift, >> keeps the sign int reverseBits(int x) { int out = 0; for (int i = 0; i < 32; i++) { out = (out << 1) | (x & 1); x >>>= 1; // MUST be >>>, or negatives loop forever } return out; } // Left-shifting into the sign bit is defined in Java, undefined in C++.
Gotchas
- Operator precedence.
a & 1 == 0meansa & (1 == 0). Parenthesize every bitwise comparison. - Python has no
>>>.-8 >> 1is-4and stays negative forever. Mask with0xFFFFFFFFto emulate a logical shift. - Python ints never overflow, so 32-bit problems need explicit masking and sign reinterpretation.
1 << 31overflows a Java or C++int. Use1L << 31for 64-bit shifts, and note1 << 64is undefined in C++.- Shift counts must be in range. Shifting by ≥ the type width is undefined behavior in C and C++.
x & (x - 1) == 0claims 0 is a power of two. Always guard withx > 0.__builtin_ctz(0)is undefined. So is__builtin_clz(0).~xis not-x. It is-x - 1.x >> kversusx // 2^kon negatives. Python's>>floors, matching//. C and C++ integer division truncates toward zero, so they differ.- Bitmask DP is only viable to n ≈ 20–22.
2²⁵states already exceeds typical memory limits. - XOR cannot count. It detects odd occurrence counts only. For "appears three times" you need bit counting mod 3.