Bit Manipulation & Math
One-liner: treat numbers as rows of bits or as digits you can peel off one at a time, and use a handful of identities —
x ^ x = 0,n & (n-1),x % 10— to solve in O(1) extra space what would otherwise need a map.
The analogy
A number is a row of light switches. XOR flips a switch only where the other row has an "on" — do it twice with the same row and every switch is back where it started. That is why XOR-ing a list where everything appears twice leaves only the loner: the pairs undo each other. For math problems, think of long multiplication on paper: you work digit by digit, carrying to the next column.
Recognition signals
- "Every element appears twice except one", or "find the missing number" with an O(1) space demand → XOR.
- "Count the 1 bits", powers of two, reversing bits, or "without using
+" → bit tricks. - A number too large for an integer, given as a string → digit-by-digit arithmetic.
- A matrix that must be changed in place, or walked in a spiral → index and boundary bookkeeping.
- A sequence of numbers that might repeat forever (Happy Number) → cycle detection, as in Linked List.
Step-by-step walkthrough
Single Number on [4, 1, 2, 1, 2]:
- Start with
result = 0. 0 ^ 4 = 4. Then4 ^ 1 = 5,5 ^ 2 = 7.7 ^ 1 = 6(the 1 cancels).6 ^ 2 = 4(the 2 cancels).- Every pair has cancelled;
4is left.
Code template
XOR cancels pairs, and the same trick finds a missing number when you XOR indices with values:
// SingleNumber (LeetCode 136): every value appears twice except one.
// x ^ x == 0 and x ^ 0 == x, so XOR-ing everything cancels the pairs.
func SingleNumber(nums []int) int {
result := 0
for _, n := range nums {
result ^= n
}
return result
}
// MissingNumber (LeetCode 268): XOR every index and every value; only the missing number is left unpaired.
func MissingNumber(nums []int) int {
result := len(nums)
for i, n := range nums {
result ^= i ^ n
}
return result
}result ^= nXOR is commutative and every value cancels with its twin, so the order never matters and the answer falls out at the end.
result := len(nums)For Missing Number the indices are 0..n-1 but the values are 0..n. Starting from n pairs it with the one index that does not exist.
The real solutions
Counting bits. n & (n-1) erases the lowest 1-bit, so counting how often you can do it counts the bits:
// HammingWeight (LeetCode 191): n & (n-1) clears the lowest set bit, so the loop runs once per 1-bit.
func HammingWeight(n uint32) int {
count := 0
for n != 0 {
n &= n - 1
count++
}
return count
}
// CountBits (LeetCode 338): bits of i = bits of i/2 (shift right) plus its last bit.
func CountBits(n int) []int {
bits := make([]int, n+1)
for i := 1; i <= n; i++ {
bits[i] = bits[i>>1] + i&1
}
return bits
}Reversing bits pushes the last bit of n onto the result 32 times:
// ReverseBits (LeetCode 190): peel the lowest bit off n and push it onto the result from the right.
func ReverseBits(n uint32) uint32 {
var result uint32
for range 32 {
result = result<<1 | n&1
n >>= 1
}
return result
}Adding without +. XOR is addition ignoring carries; AND shifted left is exactly the carries. Repeat until no carry is left:
// GetSum (LeetCode 371): add without + or -. XOR is addition without carry, AND shows where the carries go.
func GetSum(a, b int32) int32 {
for b != 0 {
carry := uint32(a&b) << 1
a ^= b
b = int32(carry)
}
return a
}Reverse Integer. Peel digits with % 10, but test for overflow before multiplying:
// Reverse (LeetCode 7): reverse the digits of a 32-bit integer, or 0 if the result would overflow.
// The overflow check happens BEFORE multiplying, because afterwards it is too late.
func Reverse(x int) int {
rev := 0
for x != 0 {
digit := x % 10
x /= 10
if rev > math.MaxInt32/10 || (rev == math.MaxInt32/10 && digit > 7) {
return 0
}
if rev < math.MinInt32/10 || (rev == math.MinInt32/10 && digit < -8) {
return 0
}
rev = rev*10 + digit
}
return rev
}Fast exponentiation halves the exponent each call, so 2^1000 needs about 10 multiplications instead of 1000:
// MyPow (LeetCode 50): fast exponentiation. x^n = (x^(n/2))^2, times x when n is odd. O(log n) multiplications.
func MyPow(x float64, n int) float64 {
if n < 0 {
return 1 / MyPow(x, -n)
}
if n == 0 {
return 1
}
half := MyPow(x, n/2)
if n%2 == 0 {
return half * half
}
return half * half * x
}Happy Number is a hidden linked list: the next number is the next node, so a repeat is a cycle:
// IsHappy (LeetCode 202): sum the squares of the digits repeatedly. Reaching 1 means happy;
// the sequence is a linked list, so a repeat means a cycle (detected with slow and fast pointers).
func IsHappy(n int) bool {
step := func(x int) int {
sum := 0
for ; x > 0; x /= 10 {
d := x % 10
sum += d * d
}
return sum
}
slow, fast := n, step(n)
for fast != 1 && slow != fast {
slow = step(slow)
fast = step(step(fast))
}
return fast == 1
}Plus One carries from the right and stops at the first digit below 9:
// PlusOne (LeetCode 66): add one to a number stored as a digit slice.
func PlusOne(digits []int) []int {
for i := len(digits) - 1; i >= 0; i-- {
if digits[i] < 9 {
digits[i]++
return digits // no carry left to pass on
}
digits[i] = 0 // 9 + 1 = 0, carry one position left
}
return append([]int{1}, digits...) // all nines: 999 -> 1000
}Rotate Image is two easy moves: transpose, then reverse each row:
// Rotate (LeetCode 48): rotate an n×n matrix 90° clockwise in place = transpose, then reverse every row.
func Rotate(m [][]int) {
n := len(m)
for i := 0; i < n; i++ {
for j := i + 1; j < n; j++ {
m[i][j], m[j][i] = m[j][i], m[i][j]
}
}
for _, row := range m {
for l, r := 0, n-1; l < r; l, r = l+1, r-1 {
row[l], row[r] = row[r], row[l]
}
}
}Spiral Matrix shrinks four boundaries. The two guarded ifs stop a single remaining row or column from being walked twice:
// SpiralOrder (LeetCode 54): walk the outer ring, then shrink the four boundaries.
func SpiralOrder(m [][]int) []int {
if len(m) == 0 {
return nil
}
top, bottom, left, right := 0, len(m)-1, 0, len(m[0])-1
var out []int
for top <= bottom && left <= right {
for c := left; c <= right; c++ {
out = append(out, m[top][c])
}
top++
for r := top; r <= bottom; r++ {
out = append(out, m[r][right])
}
right--
if top <= bottom { // a single remaining row must not be walked twice
for c := right; c >= left; c-- {
out = append(out, m[bottom][c])
}
bottom--
}
if left <= right { // same for a single remaining column
for r := bottom; r >= top; r-- {
out = append(out, m[r][left])
}
left++
}
}
return out
}Multiply Strings is long multiplication; digit i times digit j lands in column i + j + 1:
// Multiply (LeetCode 43): multiply two numbers given as strings, digit by digit like on paper.
// Digit i of a times digit j of b lands in result position i+j+1 (carry goes to i+j).
func Multiply(a, b string) string {
if a == "0" || b == "0" {
return "0"
}
res := make([]int, len(a)+len(b))
for i := len(a) - 1; i >= 0; i-- {
for j := len(b) - 1; j >= 0; j-- {
sum := int(a[i]-'0')*int(b[j]-'0') + res[i+j+1]
res[i+j+1] = sum % 10
res[i+j] += sum / 10
}
}
out := make([]byte, 0, len(res))
for i, d := range res {
if i == 0 && d == 0 {
continue // strip the single possible leading zero
}
out = append(out, byte('0'+d))
}
return string(out)
}Two problems from this group have no code here because the idea fits in a sentence. Set Matrix Zeroes: use the first row and first column as your "zero this" markers so you need no extra memory (remember to track whether those two themselves need zeroing). Detect Squares: count points in a map; for a query point, every stored point on a diagonal gives one candidate square, and you multiply the counts of the two missing corners.
Complexity
| Approach | Time | Space |
|---|---|---|
| Count with a hash map | O(n) | O(n) |
| XOR / bit tricks | O(n) | O(1) |
| Fast exponentiation | O(log n) | O(log n) recursion |
| Matrix in place | O(m·n) | O(1) |
Common mistakes
Practice ladder
- 1.Single NumberEasyWhich operation undoes itself?
- 2.Easy
- 3.Counting BitsEasyReuse the answer for a smaller number.
- 4.Easy
- 5.Happy NumberEasyWhat if the sequence never ends?
- 6.Medium
- 7.Medium
- 8.Medium
- 9.Medium
Which pattern? Drills
Every number in an array appears exactly twice except one. Find that one using no extra memory.
Which pattern?