Skip to content

Bit Manipulation & Math

Mark as:

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

  1. "Every element appears twice except one", or "find the missing number" with an O(1) space demand → XOR.
  2. "Count the 1 bits", powers of two, reversing bits, or "without using +" → bit tricks.
  3. A number too large for an integer, given as a string → digit-by-digit arithmetic.
  4. A matrix that must be changed in place, or walked in a spiral → index and boundary bookkeeping.
  5. 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]:

  1. Start with result = 0.
  2. 0 ^ 4 = 4. Then 4 ^ 1 = 5, 5 ^ 2 = 7.
  3. 7 ^ 1 = 6 (the 1 cancels). 6 ^ 2 = 4 (the 2 cancels).
  4. Every pair has cancelled; 4 is left.

Code template

XOR cancels pairs, and the same trick finds a missing number when you XOR indices with values:

SingleNumber, MissingNumber — go/bitmath/bitmath.go
// SingleNumber: 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: 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
}
1result ^= n
Why:

XOR is commutative and every value cancels with its twin, so the order never matters and the answer falls out at the end.

2result := len(nums)
Why:

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, CountBits
// HammingWeight: 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: 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
// ReverseBits: 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
// GetSum: 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
// Reverse: 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
// MyPow: 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
// IsHappy: 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
// PlusOne: 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
// Rotate: 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
// SpiralOrder: 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
// Multiply: 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

ApproachTimeSpace
Count with a hash mapO(n)O(n)
XOR / bit tricksO(n)O(1)
Fast exponentiationO(log n)O(log n) recursion
Matrix in placeO(m·n)O(1)

Common mistakes

Practice ladder

  1. 1.
    Single Number
    Which operation undoes itself?
    Easy
  2. 2.
    Number of 1 Bits
    Easy
  3. 3.
    Counting Bits
    Reuse the answer for a smaller number.
    Easy
  4. 4.
    Missing Number
    Easy
  5. 5.
    Happy Number
    What if the sequence never ends?
    Easy
  6. 6.
    Rotate Image
    Medium
  7. 7.
    Spiral Matrix
    Medium
  8. 8.
    Pow(x, n)
    Medium
  9. 9.
    Sum of Two Integers
    Medium

Which pattern? Drills

Question 1 of 5

Every number in an array appears exactly twice except one. Find that one using no extra memory.

Which pattern?