Bit Manipulation
"Thinking in bits: The shortest path to O(1) performance. XOR is your best friend in interviews."
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.
⚡ 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.
• 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 |
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.
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 |
// 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.
// 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.
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 ✓
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.
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.
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:
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.
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]
⚡ 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!
-
a ^ bcomputes the sum without any carry. -
(a & b) << 1computes the carry bits (and shifts them left so they add to the next column).
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!
📝 Assignment
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.
for(int mask=0; mask < (1<<n); mask++)
~x = -x - 1
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.