// pattern debugger≡ menu

stack>advanced patterns / bits

// Bit Manipulation

XOR cancellation, n & (n−1), and the handful of bit identities that solve an entire question category in three lines.

core idea

Operate on a number’s binary representation directly instead of the number’s value. A handful of identities let &, |, ^, <<, and >> do in one step what a loop, a hash set, or a whole extra array would otherwise cost you — usually collapsing an O(n)-space solution down to O(1):

identity what it does shows up in
x ^ x == 0 and x ^ 0 == x a value cancels itself; XOR is its own inverse pair-cancellation — “find the loner”
n & (n - 1) clears the lowest set bit popcount, power-of-two checks
n & -n isolates the lowest set bit (keeps only it) Fenwick trees, “next set bit” walks
n << k / n >> k multiply / divide by 2^k for a non-negative n fast halving in a bit-DP recurrence
1 << k a mask with only bit k set test, set, or clear one specific bit
n > 0 && (n & (n - 1)) == 0 true only when n is a power of two one-line power-of-two check
1
0
clear
1
1
0
2
0
3
n = 12 = 0b1100 — n & (n-1) clears the lowest set bit (weight 4) → 0b1000 = 8

when to reach for it

  • The problem demands no extra space, and a HashSet/Dictionary would otherwise be the obvious answer — XOR cancellation deletes it entirely.
  • You’re counting or testing individual bits: “how many 1 bits”, “is this a power of two”, “reverse the bits”.
  • The statement forbids the normal arithmetic operators and wants the result built from &, |, ^, <<, >> instead (e.g. “add two integers without +”).
  • You need an answer for every integer in a range 0..n, and each answer is one bit-shift away from a smaller integer’s answer you’ve already computed.

universal templates

XOR cancellation — pairs vanish, the loner (or the gap) survives:

public int XorCancellation(int[] nums)
{
    int result = 0;
    foreach (int x in nums)
        result ^= x;            // x^x=0 for every pair; XOR is commutative/associative,
    return result;               // so pairing order never matters
}

Clear the lowest set bit — count or process bits one at a time, not 32 times:

public int ClearLowestSetBit(uint n)
{
    int count = 0;
    while (n != 0)
    {
        n &= n - 1;              // n-1 flips every trailing 0 to 1 and the lowest 1 to 0;
        count++;                 // ANDing with the original n zeroes out just that one bit
    }
    return count;
}

Bit-DP recurrence — every value’s answer is a smaller value’s answer plus one bit’s worth:

public int[] BitDp(int n)
{
    var dp = new int[n + 1];
    for (int i = 1; i <= n; i++)
        dp[i] = dp[i >> 1] + (i & 1);   // i>>1 is smaller than i, so it's already filled in
    return dp;
}

the space tell

If a HashSet would solve it in O(n) space, ask whether the duplicates actually cancel in pairs. Two copies of the same value XOR to zero; three or more don’t, and you’ll need a different trick (per-bit counting mod 3). When pairs do cancel, you just deleted the hashmap from your solution.

problems

Four short problems, each one instance of a skeleton above:

  1. 01Single NumbereasyLC #136

    XOR everything — pairs cancel, the loner survives.

  2. 02Missing NumbereasyLC #268

    XOR indices against values — everything cancels except the gap.

  3. 03Number of 1 BitseasyLC #191

    n & (n−1) clears the lowest set bit; count the clears.

  4. 04Counting BitseasyLC #338

    DP on bits: bits[i] = bits[i >> 1] + (i & 1).

cheat sheet — bits

recognize it

  • "constant/no extra space" where a HashSet would normally solve it → look for pair-cancellation via XOR
  • "how many 1 bits", "power of two", "reverse the bits" → the n & (n - 1) family
  • problem forbids +/- and wants the result built from &, |, ^, <<, >> instead
  • an answer is needed for every integer 0..n, and each one is one bit-shift away from a smaller one already computed → bit-DP

key tricks

  • x ^ x == 0 and x ^ 0 == x — XOR cancels duplicate pairs; commutative + associative means order never matters
  • n & (n - 1) clears the lowest set bit — loop until n == 0, count the clears, done in popcount(n) steps not 32
  • n & -n isolates the lowest set bit instead of clearing it
  • seed the accumulator with n (or fold in the full index range) before XOR-ing indices against values — Missing Number
  • bits[i] = bits[i >> 1] + (i & 1) — bit-DP: drop the lowest bit, add it back if it was set

common bugs

  • assuming XOR cancellation generalizes past pairs — three copies don't vanish, x ^ x ^ x == x
  • reaching for a HashSet<int> when the prompt says "constant extra space" — it passes, but it's the O(n)-space solution you were steered away from
  • signed right shift (>>) sign-extending a negative value into an infinite loop — use uint or the >>> operator
  • off-by-one on array size — an inclusive 0..n range needs n + 1 slots, not n
  • Gauss-sum (n * (n + 1) / 2) without long — the multiply overflows int for large enough n

// connections