Progress
🌍
Why does this topic matter?
Everything a computer does — adding, comparing, storing — happens at the bit level. Knowing how to manipulate bits directly lets you write solutions that no HashMap or loop can match: O(1) space, O(1) time, using just 32 switches (the bits of an integer). XOR alone solves "find the unique element" problems in ways that seem like magic until you understand the underlying logic. Bit manipulation is tested at Amazon, Google, Apple, and Microsoft because it proves you understand computers at the hardware level.

📖 Before We Start — The Big Picture

Every number stored in memory is just a sequence of bits (binary digits: 0 or 1). An int in Java is 32 bits — 32 on/off switches. When you write int x = 13, the computer stores it as 00000000 00000000 00000000 00001101.

Bitwise operators let you flip, combine, and interrogate those switches individually. AND (&) keeps a bit only if both are 1 — it's a mask. OR (|) sets a bit if either is 1. XOR (^) is 1 when bits differ — its superpower is that the same number XORed with itself cancels to 0, which is how you find unique elements without extra space. Left shift (<<) multiplies by 2; right shift (>>) divides by 2.

🔗
How it connects: Lecture 5 (Recursion) used the include/exclude pattern for subsets recursively. This lecture shows how to generate all 2N subsets non-recursively using bit masks — same concept, different representation. Lecture 7 (Math) uses bit manipulation for fast exponentiation (binary exponentiation).

⚡ Why Bits Matter in DSA

Every number in your computer is stored as a sequence of bits (0s and 1s). Bit manipulation lets you operate on these bits directly — and these operations are the fastest thing your CPU can do.

💡
Why should you care?
• Many interview problems have elegant O(1) solutions using bit tricks
• XOR alone solves "find the unique element" in O(n) time, O(1) space — no HashMap needed
• Understanding bits is essential for understanding how Java stores int, long, negative numbers
• Bit manipulation is a favorite topic at Amazon, Google, Apple, Microsoft

🔢 Bitwise Operators — Your Toolkit

Operator Symbol What it Does Example (5 = 101, 3 = 011) Result
AND & 1 only if BOTH bits are 1 101 & 011 001 (1)
OR | 1 if EITHER bit is 1 101 | 011 111 (7)
XOR ^ 1 if bits are DIFFERENT 101 ^ 011 110 (6)
NOT ~ Flip every bit ~101 ...010 (-6)
Left Shift << Shift bits left (multiply by 2) 101 << 1 1010 (10)
Right Shift >> Shift bits right (divide by 2) 101 >> 1 10 (2)

🔥 Java Built-in Bit Methods

Interviewers know these exist. You can often use them if the question isn't specifically about implementing them from scratch.

Method What it does Equivalent To
Integer.bitCount(n) Counts number of 1s in binary representation Brian Kernighan's
Integer.highestOneBit(n) Returns an int with only the MSB set Finding highest power of 2 ≤ n
Integer.lowestOneBit(n) Returns an int with only the LSB set n & (-n)
Integer.toBinaryString(n) Returns binary string of n Awesome for debugging
🚨
DANGER: Operator Precedence!
In Java, == has higher precedence than &, ^, |. This means:
if (n & 1 == 1) is parsed as if (n & (1 == 1)) → WRONG!
Always use parentheses: if ((n & 1) == 1)

🔄 Two's Complement — How Negatives Work

Java uses two's complement to store negative numbers. To negate a number: flip all bits, then add 1.

Binary · Two's Complement
 5 in binary:  00000101
~5 (flip):    11111010
+1:           11111011  →  this is -5

// Key insight: -n == ~n + 1
// So: n & (-n) gives you the LOWEST SET BIT of n
// Example: 12 = 1100, -12 = 0100 → 12 & (-12) = 0100 = 4

This n & (-n) trick (isolating the lowest set bit) appears constantly in advanced problems like Binary Indexed Trees (Lecture 23).


🔧 Core Bit Tricks — The 6 Operations You Must Memorize

Get / Set / Clear / Toggle — The Big Four

Operation Code How it Works
Check if bit k is set (n >> k) & 1 Shift bit k to position 0, AND with 1
Set bit k to 1 n | (1 << k) OR with a mask that has only bit k set
Clear bit k to 0 n & ~(1 << k) AND with a mask that has every bit EXCEPT k
Toggle bit k n ^ (1 << k) XOR flips: 0→1, 1→0
☕ Java · All Four Operations
// n = 13 = 1101 (binary), k = 1

// CHECK bit 1:  (13 >> 1) & 1 = (110) & 1 = 0 // bit 1 is NOT set

// SET bit 1:    13 | (1 << 1) = 1101 | 0010 = 1111 = 15

// CLEAR bit 2:  13 & ~(1 << 2) = 1101 & 1011 = 1001 = 9

// TOGGLE bit 0: 13 ^ (1 << 0) = 1101 ^ 0001 = 1100 = 12

Advanced: Clearing a Range of Bits

Sometimes you need to clear all bits from the Most Significant Bit (MSB) down to index i, or from index i down to 0.

☕ Java · Range Masking
// 1. Clear all bits from MSB down to index i (inclusive)
int mask1 = (1 << i) - 1;
n = n & mask1;

// 2. Clear all bits from index i down to 0 (inclusive)
int mask2 = ~((1 << (i + 1)) - 1);
n = n & mask2;

🧮 Brian Kernighan's Algorithm — Count Set Bits in O(k)

The trick: n & (n-1) removes the lowest set bit of n. Repeat until n becomes 0. The number of iterations = number of set bits.

☕ Java · Brian Kernighan
int countSetBits(int n) {
    int count = 0;
    while (n != 0) {
        n = n & (n - 1);  // remove lowest set bit
        count++;
    }
    return count;
}

// Dry run: n = 13 (1101)
// Iteration 1: n = 1101 & 1100 = 1100 (12), count = 1
// Iteration 2: n = 1100 & 1011 = 1000 (8),  count = 2
// Iteration 3: n = 1000 & 0111 = 0000 (0),  count = 3
// Return 3 ✓
🧠
Why n & (n-1) works: Subtracting 1 from n flips all bits from the lowest set bit to the right. AND-ing with n clears the lowest set bit and everything below it. Example: 1100 & 1011 = 1000

⚡ Power of 2 Check — The One-Liner

A number is a power of 2 if and only if it has exactly one set bit.

☕ Java · Power of 2
boolean isPowerOfTwo(int n) {
    return n > 0 && (n & (n - 1)) == 0;
}

// 16 = 10000 → 16 & 15 = 10000 & 01111 = 00000 → true ✓
//  6 = 00110 →  6 &  5 = 00110 & 00101 = 00100 → false ✓
// EDGE CASE: n must be > 0! (0 is NOT a power of 2)

🎯 XOR Patterns — The Interview Favorite

XOR (^) has three magical properties that make it the most useful bitwise operator in interviews:

Property Expression Why it's useful
Self-inverse a ^ a = 0 Pairs cancel out — find unique elements!
Identity a ^ 0 = a XOR with 0 does nothing
Commutative a ^ b = b ^ a Order doesn't matter — XOR all elements freely

🔄 XOR Cancellation — Find the Unique Element

Problem: Every element in an array appears twice except one. Find the unique one in O(1) space.

☕ Java · Single Number
int singleNumber(int[] nums) {
    int result = 0;
    for (int n : nums) result ^= n;
    return result;
}

// Dry run: [4, 1, 2, 1, 2]
// 0 ^ 4 = 4
// 4 ^ 1 = 5
// 5 ^ 2 = 7
// 7 ^ 1 = 6  (1 cancelled)
// 6 ^ 2 = 4  (2 cancelled)
// Return 4 ✓ — only the unique number survives!

The "Missing Number" Extension

What if you have an array containing n distinct numbers in the range [0, n], and you need to find the missing one? (LeetCode 268)

Instead of summing everything (which risks integer overflow), you can XOR all the array values AND XOR all the numbers from 0 to n. Every number will appear twice (once in the array, once in the 0..n range) except the missing number. They all cancel out, leaving just the missing number!

🔀 XOR Partitioning — Find TWO Unique Elements

When TWO elements are unique, XOR gives us their combined XOR. We then split the array using any differing bit:

☕ Java · Two Unique Numbers
int[] twoUnique(int[] nums) {
    int xor = 0;
    for (int n : nums) xor ^= n;  // xor = a ^ b

    int rightmostBit = xor & (-xor);  // isolate lowest differing bit

    int a = 0, b = 0;
    for (int n : nums) {
        if ((n & rightmostBit) != 0) a ^= n;  // group 1
        else b ^= n;                          // group 2
    }
    return new int[]{a, b};
}

// Why this works:
// Step 1: XOR all → get a^b (some bits are set where a,b differ)
// Step 2: Pick any set bit → this bit is 1 in one number, 0 in the other
// Step 3: Split into two groups by this bit → each group has ONE unique
// Step 4: XOR each group → pairs cancel, unique survives

🎭 Bitmask Techniques

📦 Subset Generation Using Bitmask

For an array of n elements, each subset corresponds to an n-bit number (0 to 2ⁿ-1). If bit j is set, include element j.

☕ Java · Subsets via Bitmask
List<List<Integer>> subsets(int[] nums) {
    int n = nums.length;
    List<List<Integer>> result = new ArrayList<>();

    for (int mask = 0; mask < (1 << n); mask++) {
        List<Integer> subset = new ArrayList<>();
        for (int j = 0; j < n; j++) {
            if ((mask & (1 << j)) != 0) {
                subset.add(nums[j]);
            }
        }
        result.add(subset);
    }
    return result;
}

// For nums = [a, b, c], n=3, masks 0..7:
// mask=0 (000): []
// mask=1 (001): [a]
// mask=2 (010): [b]
// mask=3 (011): [a, b]
// mask=4 (100): [c]
// mask=5 (101): [a, c]
// mask=6 (110): [b, c]
// mask=7 (111): [a, b, c]
🔮
Recursion vs Bitmask for subsets: Both produce 2ⁿ subsets. Bitmask is iterative (no stack overflow risk) and often faster in practice. Use bitmask when n ≤ 20. For larger n, use recursion with pruning.

⚡ Useful Shift Tricks

Trick Code Why
Multiply by 2 n << 1 Shift left = ×2
Divide by 2 n >> 1 Shift right = ÷2 (rounds down)
Check odd/even (n & 1) Last bit: 0=even, 1=odd
Swap without temp a^=b; b^=a; a^=b; XOR swap
Lowest set bit n & (-n) Isolates rightmost 1-bit
Turn off lowest set bit n & (n-1) Brian Kernighan's trick
All 1s mask of length k (1 << k) - 1 e.g. k=4 → 1111

🧮 The Half-Adder Pattern (Addition without `+`)

This is a universally asked FAANG question (LeetCode 371). How do you add two numbers without using the + operator?

Think about how binary addition works: 1+0=1, 0+1=1, 0+0=0, 1+1=0 (with a carry of 1). Does that look familiar? It's exactly an XOR operation!

☕ Java · Sum of Two Integers
public int getSum(int a, int b) {
    while (b != 0) {
        int sum = a ^ b;            // step 1: sum without carry
        int carry = (a & b) << 1;    // step 2: find carry and shift
        a = sum;
        b = carry;                  // repeat until carry is zero
    }
    return a;
}

⏱ Complexity Patterns in Bit Manipulation

Algorithm Time Space Notes
Check/Set/Clear bit O(1) O(1) Single operation
Count set bits (naive) O(32) O(1) Check each bit
Count set bits (Kernighan) O(k) O(1) k = number of set bits
Single Number (XOR) O(n) O(1) One pass
Two Unique (XOR partition) O(n) O(1) Two passes
Generate subsets (bitmask) O(2ⁿ × n) O(1) extra Iterative, no recursion
Counting Bits 0..n (DP) O(n) O(n) bits[i] = bits[i&(i-1)] + 1

💪 In-Lecture Practice Problems

Work through these in order. Don't look at the solution before attempting!

Problem 01 · Brian Kernighan's Algorithm
Number of 1 Bits (Hamming Weight)
Easy AmazonMicrosoftApple Brian Kernighan
Problem 02 · XOR Cancellation
Single Number
Easy GoogleAmazon XOR Cancellation
Problem 03 · XOR Partitioning
Single Number III
Medium GoogleAmazon XOR Partition
Problem 04 · Bitmask Iteration
Subsets
Medium MetaAmazon Bitmask Generation
Problem 05 · XOR Partitioning
Missing Number
Easy AmazonMicrosoft XOR
Problem 06 · Hamming Distance
Power of Two
Easy GoogleAmazon Bitmasking
Problem 07 · DP + Bit Manipulation
Counting Bits
Easy AppleMicrosoft DP Build Arrays
Problem 08 · Shifting Tricks
Reverse Bits
Easy AmazonApple Right/Left Shifts
Problem 09 · String XOR
Find the Difference
Easy GoogleAmazon Cancellation
Problem 10 · Advanced Math Masking
Single Number II
Medium GoogleAmazon Buckets


📝 Assignment

📋
Lecture 3 Assignment — 30 Problems
Complete the assignment before moving to Lecture 4. It covers basic operations, XOR tricks, masks, and Brian Kernighan.

📄 Open Assignment →

✅ Lecture Completion Checklist

Check each item off as you master it. Don't proceed to Lecture 4 until all are checked.

I can set, clear, toggle, and check the kth bit correctly without googling
I understand how n & (n-1) drops the lowest set bit (Brian Kernighan)
I know why n & (-n) isolates the lowest set bit
I have solved the "Two unique numbers" problem using XOR partitioning
I can generate the power set of an array using bitmask iteration
I have completed at least 15 problems from the assignment
I can apply Brian Kernighan's algorithm to count set bits in O(k)
I know how to iterate all 2ⁿ subsets using bitmask: for(int mask=0; mask < (1<<n); mask++)
I can explain Two's Complement and why ~x = -x - 1
I know the bitmask DP state transition pattern and can apply it to subset-sum problems
🧠
You're ready for Topic 7: Mathematics for DSA
Bit manipulation unlocked: XOR, bit masking, and all the O(1) tricks. Mathematics for DSA covers the patterns that accelerate hard problems from O(n) to O(log n) — primes, GCD, modular arithmetic, combinatorics. Short and high-impact.
← Topic 5: Recursion & BacktrackingTopic 7: Mathematics for DSA →