E ExamMaster

Data Structures & Algorithms · Data Structures & Algorithms

Bit Manipulation

Bitwise operations and tricks for masks, counting bits and set operations.

Eight concepts on bitwise work — the five operators, XOR cancel tricks, test/set/clear, power-of-two and lowest-set-bit identities, Brian Kernighan popcount, and bitmasks for subsets. Interview-medium: every claim here is one you can recompute on a tiny integer.

  • Data Structures & Algorithms
  • Medium level
  • 8 concepts
  • 5 practice questions

1The five bitwise operators

Bitwise operators act on each bit of an integer. AND (\&) keeps a bit only when both sides have it — the classic mask. OR (|) turns bits on. XOR (\oplus) flips a bit where the other side is 1, so it toggles and also surfaces differences. NOT (\sim) flips every bit. Shifts move the bit pattern: left shift multiplies a non-negative n by a power of two, and arithmetic-style right shift floors a non-negative n by a power of two.

These are not Boolean short-circuit operators. They rewrite the binary representation in place, which is why masks, flags, and compact set encodings all start here.

Figure. Five operators, five jobs: mask, combine, toggle/cancel, invert, and shift — do not treat them as interchangeable.

Pick the operator by job

  1. Mask or intersectUse AND with a mask whose 1-bits mark the positions you care about.
  2. Turn bits onUse OR with a mask that has 1s only in the positions to set.
  3. Toggle or differUse XOR to flip selected bits, or to cancel equal values later.
  4. Scale by 2^kUse n \ll k or n \gg k on non-negative n instead of multiplying or dividing by 2^k.
Operator to job
OperatorPer-bit effectTypical use
AND (\&)1 only if both are 1Mask; keep selected bits
OR (|)1 if either is 1Set bits; combine flags
XOR (\oplus)1 if bits differToggle; cancel duplicates
NOT (\sim)Flip every bitBuild inverted masks
Shift \ll / \ggMove pattern by k places\times 2^k / \lfloor / 2^k \rfloor (non-negative)

Shift scales by powers of two

Evaluate 5 \ll 2 and 20 \gg 2 for non-negative integers.

  • 5 \ll 2 = 5 \times 2^220
  • 20 \gg 2 = \lfloor 20 / 2^2 \rfloor5

Pro tip. Shifts are micro-optimizations for powers of two — prefer a clear multiply when the factor is not a compile-time 2^k.

You need to keep only the lowest 4 bits of n. The right operator pattern is
  1. n \& 0b1111 — AND with a mask of four 1s
  2. n | 0b1111 — OR forces those bits on, and leaves higher bits alone incorrectly for a mask
  3. n \oplus 0b1111 — XOR toggles those bits instead of selecting them

A mask keeps bits where the mask is 1 and clears the rest — that is AND. OR would turn the low four bits on without clearing higher ones as a low-nibble extract; XOR would flip them.

2XOR cancel: find the unpaired number

XOR has two identities that make pair cancellation free: x \oplus x = 0 and x \oplus 0 = x. Folding XOR across an array therefore erases every value that appears an even number of times and leaves a value that appears once. That is the classic Single Number interview pattern — O(n) time and O(1) extra space, with no hash map.

Order does not matter: XOR is associative and commutative, so any traversal order yields the same accumulator.

Figure. XOR cancels pairs: a\oplus a=0, so a fold over a duplicated multiset leaves the singleton.

How the accumulator collapses

  1. Start at 0result = 0. XOR with 0 leaves the first value unchanged when it arrives.
  2. Fold every elementresult = result \oplus nums[i] for each index.
  3. Pairs vanishEach duplicated value meets itself in the fold and becomes 0; the unpaired value remains.

Single Number

def single_number(nums):
    result = 0
    for x in nums:
        result ^= x
    return result

Single Number

Given nums = [4, 1, 2, 1, 2] where every element appears twice except one, find the unique element.

  • 0 \oplus 44
  • 4 \oplus 15
  • 5 \oplus 27
  • 7 \oplus 16
  • 6 \oplus 24

Pro tip. XOR gives an O(n) time, O(1) space solution — no hash map or sorting needed. The same fold fails if some value appears three times; that needs a different bit-count trick.

In an array where every value appears twice except one that appears once, XOR of all elements equals
  1. The unpaired value, because each pair cancels to 0
  2. 0 always, because XOR of any list is 0
  3. The count of distinct values

x \oplus x = 0 removes pairs; x \oplus 0 = x leaves the singleton. The whole fold is not always 0 — that would require an even count of every value.

3Test, set, and clear bit i

Bit index i counts from the least-significant bit as 0. Test bit i with (n \gg i) \& 1. Set it with n | (1 \ll i). Clear it with n \& \sim(1 \ll i) — the inverted one-hot mask keeps every bit except i.

The one-hot 1 \ll i is the whole trick: build a mask with a single 1, then AND, OR, or AND-NOT against n. Getting the index off-by-one is the usual bug — index zero is the units bit, not the leftmost drawn digit.

Figure. Four cells show 1101_2 = 13. Index i grows right-to-left from the least-significant bit; bit 1 is the 0 cell.

Three masks from one-hot

  1. Build 1 \ll iA mask with only bit i set.
  2. TestShift n down by i and AND with 1 — result is 0 or 1.
  3. Set or clearOR with the one-hot to set; AND with its NOT to clear.

Edit bits of 13

Start from n = 13 (1101_2). Test bit 1, set bit 1, then clear bit 2 of the original 13.

  • (13 \gg 1) \& 10
  • 13 | (1 \ll 1)15 (1111_2)
  • 13 \& \sim(1 \ll 2)9 (1001_2)

Pro tip. Set and clear both start from the same one-hot mask; only the combining operator changes.

To clear bit i of n you compute
  1. n \& \sim(1 \ll i)
  2. n | (1 \ll i)
  3. (n \gg i) \& 1

AND with the inverted one-hot forces bit i to 0 and leaves other bits alone. OR sets the bit; the shift-and-AND form only tests it.

4Power-of-two test

A positive integer n is a power of two if and only if it has exactly one bit set. Clearing the lowest set bit with n \& (n - 1) therefore yields 0 exactly on those values: the single 1 disappears and nothing remains. The full test is n > 0 and n \& (n - 1) = 0without the positivity check, zero falsely looks like a power of two.

That is why the identity shows up in allocator alignment checks and in "is this length a power of two?" interview prompts.

Figure. A power of two is a single 1 among zeros. Clearing that 1 with n \& (n-1) leaves an all-zero word.

Apply the test

  1. Reject non-positiveIf n \le 0, it is not a power of two.
  2. Clear lowest 1Compute n \& (n - 1). Subtracting 1 flips the trailing 0s and the lowest 1.
  3. Demand zeroIf the result is 0, the original n had exactly one set bit.

8 yes, 6 no

Decide whether 8 and 6 are powers of two using n \& (n - 1).

  • 8 \& (8 - 1) = 8 \& 70 → yes (n > 0)
  • 6 \& (6 - 1) = 6 \& 54 → no

Pro tip. Always keep the n > 0 guard beside the bit test — 0 \& (0 - 1) is an implementation-defined footgun if you only quote the AND.

Which check correctly identifies powers of two among non-negative ints?
  1. n > 0 and n \& (n - 1) = 0
  2. n \& (n - 1) = 0 alone, including n = 0
  3. n | (n - 1) = n

Exactly one set bit plus positivity. Dropping n > 0 accepts 0. The OR identity is not the standard power-of-two test.

5Clear or isolate the lowest set bit

Two identities manipulate the least-significant 1 without scanning all bits. n \& (n - 1) clears that lowest set bit and leaves every higher bit unchanged. n \& (-n) isolates it — on two's-complement integers the negation flips bits above the lowest 1, so the AND keeps only that one-hot value.

Clearing is the engine inside Brian Kernighan's popcount; isolating is the engine inside "rightmost flag" loops and some Fenwick-tree index steps.

Figure. Three rows for n = 12 (1100_2): source, clear-lowest (1000_2 = 8), and isolate-lowest (0100_2 = 4). Clear removes the bit from n; isolate extracts it as a one-hot mask.

Same n, two results

  1. Clearn \& (n - 1) turns the lowest 1 into 0.
  2. Isolaten \& (-n) returns a value with only that lowest 1 set.
  3. Do not confuse themClear removes the bit from n; isolate extracts the bit as its own mask.
Lowest-set-bit identities
ExpressionResultUse
n \& (n - 1)n with lowest 1 clearedPopcount loop; strip flags one by one
n \& (-n)One-hot mask of lowest 1Rightmost flag; tree index steps

Lowest bit of 12

For n = 12 (1100_2), clear the lowest set bit and isolate it.

  • 12 \& (12 - 1) = 12 \& 118 (1000_2)
  • 12 \& (-12)4 (0100_2)

Pro tip. After a clear, the new lowest set bit is the next 1 to the left — that is why repeating clear walks every set bit.

n \& (-n) returns
  1. A one-hot mask of the lowest set bit of n
  2. n with the lowest set bit cleared
  3. Always 0

Isolate keeps only the lowest 1. Clearing is n \& (n - 1), not n \& (-n).

6Count set bits in O(popcount)

Brian Kernighan's loop counts 1-bits by repeatedly clearing the lowest set bit: while n \neq 0, do n \&= (n - 1) and increment a counter. Each iteration removes exactly one 1, so the loop runs once per set bitO(\mathrm{popcount}(n)), not O(32) or O(64) for a fixed word width.

When the density of 1s is low, that beats scanning every bit position. Hardware `popcount` instructions are faster still when available; the loop is the portable interview form of the same idea.

Reuse the clear-lowest strip from Clear or isolate the lowest set bit: each x\ &=\ x-1 clears one set bit; the loop count equals the popcount.

Walk the 1s

  1. Count at 0Initialize count = 0.
  2. Clear lowest 1Replace n with n \& (n - 1) and add 1 to count.
  3. Stop at 0When n becomes 0 every set bit has been removed; return count.

Brian Kernighan popcount

def popcount(n):
    count = 0
    while n:
        n &= n - 1
        count += 1
    return count

Count set bits in 13

Count the number of 1 bits in the binary representation of 13 (1101_2).

  • 13 \& 1212 (1100_2), count = 1
  • 12 \& 118 (1000_2), count = 2
  • 8 \& 70, count = 3

Pro tip. n \&= (n-1) clears the lowest set bit, so the loop runs only as many times as there are 1s, not for all 32 bits.

Brian Kernighan's popcount on a word with k set bits runs in
  1. O(k) iterations — one clear per set bit
  2. O(32) always, regardless of k
  3. O(\log n) binary-search steps over bit positions

Each iteration clears one 1. A fixed 32-step scan is the naive width walk; there is no binary search over bit indices here.

7Bitmasks as subsets

A bitmask packs a subset of up to about 32 or 64 indexed elements into one integer: each bit flags whether that index is in the subset. Membership is (mask \gg i) \& 1. Union is OR, intersection is AND, and symmetric difference is XOR — each O(1) on a machine word.

That encoding is why subset DP and many combinatorial searches store state as an integer rather than as a hash set of indices.

Figure. Three cells for a 3-element universe. Mask 0b101 has item 0 and item 2 in the subset; item 1 is absent.

Set algebra on bits

  1. EncodeTurn membership of index i into bit i of an integer mask.
  2. CombineUnion |, intersection \&, toggle membership with XOR against 1 \ll i.
  3. QueryTest bit i for membership in O(1).
Subset ops on masks
Set operationBit expressionCost
Membership of i(mask \gg i) \& 1O(1)
Uniona | bO(1)
Intersectiona \& bO(1)
Add / remove iOR / AND-NOT with 1 \ll iO(1)

Mask 0b101 on three items

Items are indexed 0, 1, 2. Mask 0b101 encodes a subset. Which indices are present, and what are 0b101 | 0b010 and 0b101 \& 0b110?

  • bits of 0b101{0, 2} present; 1 absent
  • 0b101 | 0b0100b111 = {0, 1, 2}
  • 0b101 \& 0b1100b100 = {2}

Pro tip. Represent small subsets as an integer bitmask to make union, intersection, and membership tests O(1).

Intersection of two bitmasks is
  1. AND — bits that are 1 in both masks
  2. OR — bits that are 1 in either mask
  3. XOR — bits that differ

Intersection keeps elements in both sets, which is AND on membership bits. OR is union; XOR is symmetric difference.

8Enumerate masks: subset DP shape

Once subsets are integers from 0 to 2^n - 1, many DP transitions scan every mask and every element: for mask in 0 \ldots 2^n - 1, for i in 0 \ldots n - 1, update a state from mask with bit i flipped on or off. That double loop is the standard subset-DP budget — exponential in n, times a linear scan of bit positions — when n is about 20 or less.

The bit encoding is what makes the outer index a contiguous integer range. Without masks you would hash sets; with masks the array dp[1 \ll n] is enough.

Figure. For n=3 the outer index is the eight masks 0…7. On the current mask the inner loop visits each of the n bit positions — here three visits under mask 5. Interview n near 20 is the same shape, not a longer row.

The O(2^n n) skeleton

  1. Allocate by maskIndex DP by the integer mask, size 2^n.
  2. Scan masksIterate mask from 0 to 2^n - 1 (often in an order that respects transitions).
  3. Try each bitFor each i, read or write the neighbour mask with bit i changed — n work per mask.

Scan all masks and bits

def for_each_mask_bit(n, visit):
    for mask in range(1 << n):
        for i in range(n):
            visit(mask, i, (mask >> i) & 1)
Iterating every subset mask and every element index for n items costs
  1. O(2^n \times n)
  2. O(n^2)
  3. O(2^n) with no factor of n

2^n masks and n bit positions per mask. Dropping the inner loop undercounts; an n^2 bound ignores the exponential mask space.

Notes

  • Bitwise Operators: AND (&) masks bits, OR (|) sets bits, XOR (^) toggles/finds differences, NOT (~) flips, and shifts (<<, >>) multiply or divide by powers of two.
  • XOR Properties: x ^ x = 0 and x ^ 0 = x, so XORing all elements cancels pairs and isolates a single unpaired number.
  • Check/Set/Clear a Bit: Test bit i with (n >> i) & 1, set it with n | (1 << i), and clear it with n & ~(1 << i).
  • Power of Two Test: A positive n is a power of two iff n & (n-1) == 0, because it has exactly one set bit.
  • Bitmasking: A bitmask represents a subset of up to ~32/64 elements as bits, enabling subset DP and fast set operations.

Formulas

  • Left shift: n << k = n \times 2^k; right shift: n >> k = \lfloor n / 2^k \rfloor for non-negative n.
  • Clear the lowest set bit: n & (n-1); isolate the lowest set bit: n & (-n).
  • Counting set bits (Brian Kernighan): O(\text{number of set bits}) via repeated n &= (n-1).
  • Power of two: n > 0 and n & (n-1) == 0.
  • Subset bitmask DP: O(2^n \times n) over all subsets of n items.

Exam traps & shortcuts

  • To find the single non-duplicated number when all others appear twice, XOR the whole array - pairs cancel to 0.
  • Use n & (n-1) to clear the lowest set bit; looping this counts set bits in O(popcount) time.
  • Multiply/divide by powers of two with shifts (x << 3 for x*8) when micro-optimizing.
  • Represent small subsets as an integer bitmask to make union (|), intersection (&), and membership tests O(1).

Reference tables

Restated from the concepts above — scan sheet, not a second argument.

Identity cheatsheet
IdentityFormUse
Left / right shiftn \ll k = n \times 2^k; n \gg k = \lfloor n / 2^k \rfloor (non-negative n)Scale by powers of two
Clear lowest set bitn \& (n - 1)Popcount; strip flags
Isolate lowest set bitn \& (-n)One-hot of rightmost 1
Power of twon > 0 and n \& (n - 1) = 0Alignment / size checks
Subset DP scanO(2^n \times n) over masks and bitsDP on subsets of n items

Which operator for which job — same roster as the opening concept.

Operator and mask quick map
JobPattern
Mask / keep bitsn \& \mathrm{mask}
Set bit in | (1 \ll i)
Clear bit in \& \sim(1 \ll i)
Test bit i(n \gg i) \& 1
Cancel pairsFold XOR across the array
Subset union / intersectiona | b / a \& b

Recap

Read only this the night before a bit-manipulation round.

Ops
AND masks, OR sets, XOR toggles/cancels, NOT flips, shifts scale by 2^k.
XOR
x \oplus x = 0, x \oplus 0 = x — fold the array to find the unpaired value.
Bit i
Test (n \gg i)\&1; set n|(1\ll i); clear n\&\sim(1\ll i).
Power of two
n > 0 and n\&(n-1)=0 — exactly one set bit.
Lowest 1
Clear with n\&(n-1); isolate with n\&(-n).
Popcount
Kernighan: loop n\&=(n-1)O(\mathrm{popcount}).
Bitmask
Bit i ↔ element i; union |, intersection \&; subset DP O(2^n n).

Practise Bit Manipulation

Reading is free and needs no account. Practice, mocks and progress live in the app.

  • 5 exam-style questions on this topic, with explanations
  • A 6-question practice set that ends the chapter
  • Timed mocks scored with the real marking scheme
  • Readiness tracked per topic, kept on your device
Continue with Google — freeNo card, no trial. Works offline once installed.