name: Bit Manipulation slug: bit-manipulation difficulty_level: 2 description: > Techniques using binary operations (AND, OR, XOR, NOT, shifts) to solve problems efficiently, often achieving O(1) space where other approaches need O(n). when_to_use: | - Finding unique elements when pairs cancel out (XOR) - Checking powers of 2 (n & (n-1) == 0) - Counting set bits (Brian Kernighan's algorithm) - Swapping without temp variable (a ^= b; b ^= a; a ^= b) - Problems requiring O(1) extra space - Toggling or flipping bits metaphor: | Think of XOR like a light switch. Flip it once (XOR with a number) and the light turns on. Flip it again with the same number and it turns off. Numbers that appear twice "flip twice" and cancel out, leaving only the single number. Another analogy: imagine each number is a musical note. Playing the same note twice creates silence (destructive interference). When you play all notes together, pairs cancel out, and only the unique note remains audible. core_concept: | Bit manipulation uses properties of binary operations to solve problems elegantly. The most important operations: **XOR (⊕):** - `a ⊕ a = 0` — any number XORed with itself is zero - `a ⊕ 0 = a` — XORing with zero preserves the value - XOR is commutative and associative — order doesn't matter **AND (&):** - `a & (a-1)` removes the lowest set bit - `a & 1` checks if a number is odd **OR (|):** - `a | (1 << k)` sets the k-th bit **Shifts:** - `a << k` multiplies by 2^k - `a >> k` divides by 2^k (floor) visualization: | **XOR Cancellation Example:** ``` nums = [2, 2, 1] Step 1: result = 0 0000 XOR 0010 = 0010 (result = 2) Step 2: result = 2 0010 XOR 0010 = 0000 (pair cancels!) Step 3: result = 0 0000 XOR 0001 = 0001 (result = 1) Answer: 1 (the single number) ``` **Bit-by-bit XOR:** ``` 0010 (2) ⊕ 0010 (2) -------- 0000 (0) — same bits cancel! 0000 (0) ⊕ 0001 (1) -------- 0001 (1) — XOR with 0 preserves ``` code_template: | # XOR all elements - pairs cancel, unique remains def find_single(nums: list[int]) -> int: result = 0 for num in nums: result ^= num return result # Check if n is a power of 2 def is_power_of_two(n: int) -> bool: return n > 0 and (n & (n - 1)) == 0 # Count number of 1 bits (Brian Kernighan) def count_bits(n: int) -> int: count = 0 while n: n &= (n - 1) # Remove lowest set bit count += 1 return count # Get k-th bit (0-indexed from right) def get_bit(n: int, k: int) -> int: return (n >> k) & 1 # Set k-th bit def set_bit(n: int, k: int) -> int: return n | (1 << k) # Clear k-th bit def clear_bit(n: int, k: int) -> int: return n & ~(1 << k) # Toggle k-th bit def toggle_bit(n: int, k: int) -> int: return n ^ (1 << k) # Swap without temp (XOR swap) def swap(a: int, b: int) -> tuple[int, int]: a ^= b b ^= a a ^= b return a, b # Find two single numbers (all others appear twice) def find_two_singles(nums: list[int]) -> list[int]: # XOR all numbers - result is xor of the two singles xor_all = 0 for num in nums: xor_all ^= num # Find a bit where the two singles differ diff_bit = xor_all & (-xor_all) # Lowest set bit # Partition numbers by this bit and XOR each group a, b = 0, 0 for num in nums: if num & diff_bit: a ^= num else: b ^= num return [a, b] recognition_signals: - "find the single/unique element" - "every element appears twice except one" - "O(1) extra space" - "XOR" - "bit manipulation" - "without using extra memory" - "power of 2" - "count bits" - "binary representation" - "toggle" - "swap without temp" common_mistakes: - title: Forgetting XOR order doesn't matter description: | XOR is commutative (a⊕b = b⊕a) and associative ((a⊕b)⊕c = a⊕(b⊕c)). You can process elements in any order and still get the same result. fix: | Trust the properties. The order of XOR operations doesn't affect the final result. Focus on the logic, not the sequence. - title: Using addition instead of XOR description: | Addition doesn't cancel pairs. 2 + 2 = 4, not 0. XOR is special because a ⊕ a = 0 while a + a = 2a. fix: | Remember: XOR cancels identical values, addition accumulates them. Use ^= for pair cancellation, not +=. - title: Forgetting to handle negative numbers description: | XOR works on the binary representation. Negative numbers use two's complement, so the bit patterns are different from positive numbers. fix: | XOR still works correctly with negative numbers in most languages. The key properties (a⊕a=0, a⊕0=a) hold for any integer. - title: Not recognizing the pattern description: | Many problems hint at bit manipulation with phrases like "O(1) space" or "without extra memory" when dealing with duplicates. fix: | Look for clues: "appears twice", "unique element", "constant space", "power of 2". These often signal bit manipulation approaches. variations: - name: Single Number (pairs cancel) description: | XOR all elements to find the one that appears once when others appear twice. example: "Single Number, Single Number II (modular arithmetic)" - name: Two Single Numbers description: | Use XOR to find a differing bit, then partition elements to separate the two unique numbers. example: "Single Number III" - name: Power of Two description: | Check if n & (n-1) == 0. Powers of 2 have exactly one set bit. example: "Power of Two, Power of Four" - name: Bit Counting description: | Count set bits using n &= (n-1) to remove lowest bit each iteration. example: "Number of 1 Bits, Counting Bits" - name: Bit Masking description: | Use masks to extract, set, clear, or toggle specific bits. example: "Reverse Bits, Bitwise AND of Numbers Range" related_patterns: - greedy - dynamic-programming prerequisite_patterns: []