Data Structures & Algorithms Cheatsheet
Math and Number Theory
Use this Data Structures & Algorithms reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Complexity
| Task | Time | Note |
|---|---|---|
| GCD (Euclid) | O(log min(a, b)) | the workhorse |
| LCM | O(log min(a, b)) | via GCD |
| modular exponentiation | O(log e) | square and multiply |
| modular inverse (Fermat) | O(log m) | prime modulus only |
| extended Euclid | O(log min(a, b)) | inverse for any coprime modulus |
| primality, trial division | O(√n) | fine to n ≈ 10¹² |
| Miller-Rabin (deterministic to 2⁶⁴) | O(k log³ n) | 7 fixed bases suffice |
| Sieve of Eratosthenes to n | O(n log log n) | O(n) space |
| linear sieve with smallest prime factor | O(n) | gives factorization in O(log n) |
| factorize one n | O(√n) | O(log n) after a sieve |
| all divisors of n | O(√n) | |
| factorials and binomials mod p | O(n) precompute, O(1) query | p prime |
| Catalan number C(n) | O(n) |
GCD, LCM, And Extended Euclid
from math import gcd, lcm # lcm is Python 3.9+ def gcd_iter(a, b): while b: a, b = b, a % b return abs(a) def lcm_safe(a, b): """Divide FIRST to avoid overflow in fixed-width languages.""" return 0 if a == 0 or b == 0 else abs(a // gcd(a, b) * b) def ext_gcd(a, b): """Returns (g, x, y) with a*x + b*y = g = gcd(a, b).""" if b == 0: return a, 1, 0 g, x1, y1 = ext_gcd(b, a % b) return g, y1, x1 - (a // b) * y1 def mod_inverse(a, m): """Inverse of a mod m for any coprime m. None if not invertible.""" g, x, _ = ext_gcd(a % m, m) return None if g != 1 else x % m
Facts worth knowing:
gcd(a, b) * lcm(a, b) = a * bfor positive a and b.gcd(a, 0) = a, so the loop terminates naturally.- Bezout:
a*x + b*y = gcd(a, b)always has an integer solution, anda*x + b*y = cis solvable exactly whengcd(a, b)divides c. - Fractions reduce by dividing both parts by their GCD, which is how you canonicalize slopes in "points on a line" problems.
Modular Arithmetic
Competitive problems almost always ask for an answer mod 10⁹ + 7 (a prime) or 998244353 (prime, NTT-friendly).
MOD = 10 ** 9 + 7 (a + b) % MOD (a - b) % MOD # Python keeps this non-negative (a * b) % MOD pow(a, e, MOD) # fast modular exponentiation, O(log e) pow(a, MOD - 2, MOD) # modular inverse, prime MOD (Fermat) (a * pow(b, MOD - 2, MOD)) % MOD # a / b mod a prime MOD def mod_pow(base, exp, mod): """Square and multiply, written out.""" result = 1 base %= mod while exp > 0: if exp & 1: result = result * base % mod base = base * base % mod exp >>= 1 return result
// Java: normalize subtraction, and use long for every multiplication long sub = ((a - b) % MOD + MOD) % MOD; long mul = a % MOD * (b % MOD) % MOD; // int * int overflows, cast to long long inv = BigInteger.valueOf(a) .modInverse(BigInteger.valueOf(MOD)).longValue();
| Rule | Form |
|---|---|
addition and multiplication distribute over % | (a op b) % m = ((a % m) op (b % m)) % m |
| subtraction needs normalizing outside Python | ((a - b) % m + m) % m |
| division requires an inverse | a * inverse(b) % m, only if gcd(b, m) = 1 |
| Fermat's little theorem | a^(p-1) ≡ 1 (mod p) for prime p, a not divisible by p |
| Euler's theorem | a^φ(m) ≡ 1 (mod m) when gcd(a, m) = 1 |
There is no modular division. (a / b) % m is a * b⁻¹ % m, and b⁻¹ exists only when b and m are coprime.
Primes
def is_prime(n): """Trial division by 6k ± 1. O(sqrt n).""" if n < 2: return False if n < 4: return True if n % 2 == 0 or n % 3 == 0: return False i = 5 while i * i <= n: if n % i == 0 or n % (i + 2) == 0: return False i += 6 return True def sieve(n): """All primes up to n. O(n log log n).""" if n < 2: return [] flags = bytearray([1]) * (n + 1) flags[0] = flags[1] = 0 for i in range(2, int(n ** 0.5) + 1): if flags[i]: flags[i * i::i] = bytearray(len(flags[i * i::i])) return [i for i in range(n + 1) if flags[i]] def linear_sieve(n): """Smallest prime factor for every value up to n. O(n). Gives O(log n) factorization afterwards.""" spf = list(range(n + 1)) primes = [] for i in range(2, n + 1): if spf[i] == i: primes.append(i) for p in primes: if p > spf[i] or i * p > n: break spf[i * p] = p return spf, primes def factorize(n): """Prime factorization as {prime: exponent}. O(sqrt n).""" out = {} d = 2 while d * d <= n: while n % d == 0: out[d] = out.get(d, 0) + 1 n //= d d += 1 if d == 2 else 2 # skip evens after 2 if n > 1: out[n] = out.get(n, 0) + 1 return out def factorize_with_spf(n, spf): """O(log n) once a linear sieve is built.""" out = {} while n > 1: p = spf[n] while n % p == 0: out[p] = out.get(p, 0) + 1 n //= p return out def divisors(n): """All divisors, sorted. O(sqrt n).""" small, large = [], [] i = 1 while i * i <= n: if n % i == 0: small.append(i) if i != n // i: large.append(n // i) i += 1 return small + large[::-1] def euler_phi(n): """Count of integers in [1, n] coprime to n.""" result = n p = 2 while p * p <= n: if n % p == 0: while n % p == 0: n //= p result -= result // p p += 1 if n > 1: result -= result // n return result
From the factorization n = p₁^a₁ · … · p_k^a_k:
| Quantity | Formula |
|---|---|
| number of divisors | (a₁ + 1) · … · (a_k + 1) |
| sum of divisors | Π (p^(a+1) − 1) / (p − 1) |
| Euler's totient φ(n) | n · Π (1 − 1/p) |
| n is a perfect square | every exponent is even |
Combinatorics
from math import comb, perm, factorial comb(n, k) # C(n, k), exact for any size in Python perm(n, k) # n! / (n-k)! factorial(n) MOD = 10 ** 9 + 7 class Binomial: """Precompute factorials for O(1) C(n, k) mod a prime.""" def __init__(self, n, mod=MOD): self.mod = mod self.fact = [1] * (n + 1) for i in range(1, n + 1): self.fact[i] = self.fact[i - 1] * i % mod self.inv_fact = [1] * (n + 1) self.inv_fact[n] = pow(self.fact[n], mod - 2, mod) for i in range(n, 0, -1): self.inv_fact[i - 1] = self.inv_fact[i] * i % mod def C(self, n, k): if k < 0 or k > n: return 0 return (self.fact[n] * self.inv_fact[k] % self.mod * self.inv_fact[n - k] % self.mod) def pascal_row(n): """Row n of Pascal's triangle, O(n) with no division.""" row = [1] for k in range(n): row.append(row[-1] * (n - k) // (k + 1)) return row def catalan(n): """C(2n, n) / (n + 1). Counts balanced parentheses, BST shapes, triangulations, and monotone lattice paths below the diagonal.""" return comb(2 * n, n) // (n + 1)
| Count | Formula |
|---|---|
| permutations of n | n! |
| k-permutations of n | n! / (n − k)! |
| combinations C(n, k) | n! / (k! (n − k)!) |
| combinations with repetition | C(n + k − 1, k) |
| subsets of an n-set | 2ⁿ |
| arrangements with duplicates | n! / (a! b! c! …) |
| Catalan number Cₙ | C(2n, n) / (n + 1) |
| derangements Dₙ | n! · Σ (−1)^k / k! |
| stars and bars, n items into k bins | C(n + k − 1, k − 1) |
Pascal's identity C(n, k) = C(n−1, k−1) + C(n−1, k) is what makes binomials computable by DP without any division, which matters when the modulus is not prime.
Number Bases And Digits
bin(255), oct(255), hex(255) # '0b11111111', '0o377', '0xff' int("ff", 16) # 255 int("1010", 2) # 10 format(255, "b") # '11111111', no prefix format(255, "08b") # zero-padded def to_base(n, base): """n >= 0, base 2..36.""" if n == 0: return "0" digits = "0123456789abcdefghijklmnopqrstuvwxyz" out = [] while n: n, r = divmod(n, base) out.append(digits[r]) return "".join(reversed(out)) def digit_sum(n): total = 0 while n: n, r = divmod(n, 10) total += r return total def digit_count(n): """Number of decimal digits without string conversion.""" import math return 1 if n == 0 else math.floor(math.log10(abs(n))) + 1 def reverse_number(n): """Guard 32-bit overflow before the last multiplication.""" sign = -1 if n < 0 else 1 n, out = abs(n), 0 while n: n, d = divmod(n, 10) if out > (2 ** 31 - 1 - d) // 10: return 0 # would overflow out = out * 10 + d return sign * out
Integer Pitfalls And Useful Formulas
# Python floors, C/C++/Java truncate toward zero -7 // 2 # -4 in Python # -7 / 2 is -3 in C, C++, and Java # ceiling division, integer only, positive operands (a + b - 1) // b -(-a // b) # works for negatives in Python too # integer square root, exact, no float error import math math.isqrt(n) # floor(sqrt(n)), never off by one def is_perfect_square(n): if n < 0: return False r = math.isqrt(n) return r * r == n # overflow-safe midpoint mid = lo + (hi - lo) // 2 # Sum formulas # 1 + 2 + ... + n = n(n+1)/2 # 1^2 + 2^2 + ... + n^2 = n(n+1)(2n+1)/6 # 1 + 3 + 5 + ... + (2n-1) = n^2 # geometric: a(r^n - 1)/(r - 1) for r != 1
| Constant | Value | Why it matters |
|---|---|---|
2³¹ − 1 | 2,147,483,647 | Java and C++ int max |
2⁶³ − 1 | ≈ 9.22 × 10¹⁸ | long max, the ceiling for sums |
10⁹ + 7 | prime | the standard modulus |
2⁵³ | ≈ 9 × 10¹⁵ | largest exactly representable integer in a float64 |
Never use floats for exact integer math. int(math.sqrt(n)) can be off by one near perfect squares because of rounding. Use math.isqrt. Similarly math.log2 on large integers can round the wrong way, so use bit_length.
Gotchas
int(math.sqrt(n))is unreliable. Usemath.isqrt(n)for an exact floor.- Negative modulo differs by language. Python
-7 % 3is2, Java and C++ give-1. Normalize with((x % m) + m) % m. - Integer division direction differs. Python floors, C-family truncates.
-7 // 2versus-7 / 2give-4and-3. lcmoverflows if you computea * bfirst. Divide by the GCD first.- Modular inverse needs coprimality. Fermat's
pow(a, m - 2, m)requires a prime modulus. For a composite one use extended Euclid. math.combis exact in Python butn!in a fixed-width language overflows past 20!. Precompute modular factorials instead.- Sieve bounds.
range(2, isqrt(n) + 1)for the outer loop, and start marking ati * i. Starting at2 * istill works but wastes time. - 1 is not prime, and 2 is the only even prime. Both cases break naive loops.
- Floats lose integers past 2⁵³. Any exact arithmetic above that must stay in integers.
- `x 0.5
versusisqrt`.** The former returns a float and silently loses precision on values above 10¹⁵.