Bit manipulation shows up in interviews to test your understanding of how numbers work at the binary level. These problems often have elegant O(1) or O(log n) solutions. Math-based questions — GCD, primes, modular arithmetic — appear less frequently but are expected at the senior level.
XOR has two properties: a xor a = 0 (same values cancel) and a xor 0 = a (identity). XOR all elements — duplicates cancel out, leaving the unique number. Time O(n), space O(1).
fun singleNumber(nums: IntArray): Int {
var result = 0
for (num in nums) result = result xor num
return result
}
A power of two has exactly one bit set. n and (n - 1) clears that bit. So check n > 0 && n and (n - 1) == 0.
fun isPowerOfTwo(n: Int): Boolean {
return n > 0 && (n and (n - 1)) == 0
}
n and (n - 1) clears the lowest set bit. Count how many times until n becomes zero. Time O(k) where k is the number of set bits.
fun hammingWeight(n: Int): Int {
var num = n
var count = 0
while (num != 0) {
num = num and (num - 1)
count++
}
return count
}
XOR all numbers 0 to n with all array elements. Duplicates cancel, leaving the missing one. Time O(n), space O(1).
fun missingNumber(nums: IntArray): Int {
var xor = nums.size
for (i in nums.indices) {
xor = xor xor i xor nums[i]
}
return xor
}
and) — Both bits 1. Used for maskingor) — Either bit 1. Used for setting bitsxor) — Exactly one bit 1. Used for toggling and finding unique elementsinv()) — Flips all bitsshl) — Multiply by 2^nshr) — Divide by 2^n. Kotlin also has ushr for unsigned right shift(n shr i) and 1n or (1 shl i)n and (1 shl i).inv()n xor (1 shl i)fun checkBit(n: Int, i: Int): Boolean = (n shr i) and 1 == 1
fun setBit(n: Int, i: Int): Int = n or (1 shl i)
fun clearBit(n: Int, i: Int): Int = n and (1 shl i).inv()
fun toggleBit(n: Int, i: Int): Int = n xor (1 shl i)
XOR all elements to get a xor b. Find any set bit (xorAll and -xorAll isolates the lowest). Split numbers into two groups by that bit. XOR within each group gives a and b.
fun singleNumbers(nums: IntArray): IntArray {
var xorAll = 0
for (num in nums) xorAll = xorAll xor num
val diffBit = xorAll and (-xorAll)
var a = 0
var b = 0
for (num in nums) {
if (num and diffBit != 0) a = a xor num
else b = b xor num
}
return intArrayOf(a, b)
}
DP approach: countBits[i] = countBits[i shr 1] + (i and 1). Time O(n), space O(n).
fun countBits(n: Int): IntArray {
val result = IntArray(n + 1)
for (i in 1..n) {
result[i] = result[i shr 1] + (i and 1)
}
return result
}
A bitmask integer where each bit represents inclusion. For n elements, 2^n subsets represented by integers 0 to 2^n - 1.
fun generateSubsets(nums: IntArray): List<List<Int>> {
val result = mutableListOf<List<Int>>()
val n = nums.size
for (mask in 0 until (1 shl n)) {
val subset = mutableListOf<Int>()
for (i in 0 until n) {
if ((mask shr i) and 1 == 1) subset.add(nums[i])
}
result.add(subset)
}
return result
}
GCD(a, b) = GCD(b, a % b). Keep reducing until b becomes zero. Time O(log(min(a, b))).
fun gcd(a: Int, b: Int): Int {
var x = a
var y = b
while (y != 0) {
val temp = y
y = x % y
x = temp
}
return x
}
fun lcm(a: Int, b: Int): Int = a / gcd(a, b) * b
Mark all indices as prime. Starting from 2, mark all multiples as not prime. Start marking from p^2 since smaller multiples were handled. Time O(n log log n).
fun sieveOfEratosthenes(n: Int): List<Int> {
val isPrime = BooleanArray(n + 1) { it >= 2 }
var i = 2
while (i * i <= n) {
if (isPrime[i]) {
var j = i * i
while (j <= n) {
isPrime[j] = false
j += i
}
}
i++
}
return (2..n).filter { isPrime[it] }
}
Extract each bit, shift result left, OR the bit in.
fun reverseBits(n: Int): Int {
var input = n
var result = 0
for (i in 0 until 32) {
result = (result shl 1) or (input and 1)
input = input shr 1
}
return result
}
XOR them — result has 1s where they differ. Count the set bits.
fun hammingDistance(x: Int, y: Int): Int {
var xor = x xor y
var count = 0
while (xor != 0) {
xor = xor and (xor - 1)
count++
}
return count
}
XOR gives sum without carries. AND + left shift gives carry. Repeat until no carry.
fun getSum(a: Int, b: Int): Int {
var x = a
var y = b
while (y != 0) {
val carry = x and y
x = x xor y
y = carry shl 1
}
return x
}
Binary exponentiation — square the base and halve the exponent. If odd, multiply result by base. Time O(log b).
fun modPow(base: Long, exp: Long, mod: Long): Long {
var result = 1L
var b = base % mod
var e = exp
while (e > 0) {
if (e % 2 == 1L) result = result * b % mod
b = b * b % mod
e /= 2
}
return result
}
Keeps numbers from overflowing. Common modulus is 10^9 + 7. Key properties: (a + b) % m = ((a % m) + (b % m)) % m, same for multiplication. Subtraction needs + m to avoid negatives. Division requires modular inverse.
Count each bit position across all numbers. If count isn’t divisible by 3, that bit belongs to the unique number.
fun singleNumberII(nums: IntArray): Int {
var result = 0
for (bit in 0 until 32) {
var count = 0
for (num in nums) {
if ((num shr bit) and 1 == 1) count++
}
if (count % 3 != 0) result = result or (1 shl bit)
}
return result
}