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

TaskTimeNote
GCD (Euclid)O(log min(a, b))the workhorse
LCMO(log min(a, b))via GCD
modular exponentiationO(log e)square and multiply
modular inverse (Fermat)O(log m)prime modulus only
extended EuclidO(log min(a, b))inverse for any coprime modulus
primality, trial divisionO(√n)fine to n ≈ 10¹²
Miller-Rabin (deterministic to 2⁶⁴)O(k log³ n)7 fixed bases suffice
Sieve of Eratosthenes to nO(n log log n)O(n) space
linear sieve with smallest prime factorO(n)gives factorization in O(log n)
factorize one nO(√n)O(log n) after a sieve
all divisors of nO(√n)
factorials and binomials mod pO(n) precompute, O(1) queryp 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 * b for 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, and a*x + b*y = c is solvable exactly when gcd(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();
RuleForm
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 inversea * inverse(b) % m, only if gcd(b, m) = 1
Fermat's little theorema^(p-1) ≡ 1 (mod p) for prime p, a not divisible by p
Euler's theorema^φ(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:

QuantityFormula
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 squareevery 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)
CountFormula
permutations of nn!
k-permutations of nn! / (n − k)!
combinations C(n, k)n! / (k! (n − k)!)
combinations with repetitionC(n + k − 1, k)
subsets of an n-set2ⁿ
arrangements with duplicatesn! / (a! b! c! …)
Catalan number CₙC(2n, n) / (n + 1)
derangements Dₙn! · Σ (−1)^k / k!
stars and bars, n items into k binsC(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
ConstantValueWhy it matters
2³¹ − 12,147,483,647Java and C++ int max
2⁶³ − 1≈ 9.22 × 10¹⁸long max, the ceiling for sums
10⁹ + 7primethe 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. Use math.isqrt(n) for an exact floor.
  • Negative modulo differs by language. Python -7 % 3 is 2, Java and C++ give -1. Normalize with ((x % m) + m) % m.
  • Integer division direction differs. Python floors, C-family truncates. -7 // 2 versus -7 / 2 give -4 and -3.
  • lcm overflows if you compute a * b first. 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.comb is exact in Python but n! 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 at i * i. Starting at 2 * i still 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 versus isqrt`.** The former returns a float and silently loses precision on values above 10¹⁵.