Skip to main content
Reference

Math Cheatsheet for Algorithms

The math that algorithm problems use, grouped by topic. Each entry gives the formula, what it computes and when you need it, a short Python example with the output of a real run, and the LeetCode problems that use it.

Geometry: distances, turns and areas

Most geometry problems give integer coordinates. Keep the arithmetic in integers where you can: compare squared distances and use cross products. Then no floating-point rounding can change an answer.

Euclidean distance and squared distance

d=(x1−x2)2+(y1−y2)2d≤r  ⟺  d2≤r2d = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2} \qquad d \le r \iff d^2 \le r^2

The Euclidean distance d is the straight-line distance between two points. When you only compare distances, compare d * d instead. Squaring keeps the order of non-negative numbers. The squared distance between two points with integer coordinates is an integer, so it has no rounding error. A point is inside or on a circle of radius r exactly when its squared distance to the centre is at most r * r.

Python
Python
1def dist_sq(p, q):
2 return (p[0] - q[0]) ** 2 + (p[1] - q[1]) ** 2
3 
4 
5def in_circle(point, center, r):
6 return dist_sq(point, center) <= r * r
7 
8 
9points = [[3, 3], [5, -1], [-2, 4]]
10points.sort(key=lambda p: dist_sq(p, (0, 0)))
11print(points)
12print(in_circle((3, 4), (0, 0), 5), in_circle((3, 5), (0, 0), 5))
Output
[[3, 3], [-2, 4], [5, -1]]
True False

LeetCode problems that use it

  • K Closest Points to Origin (LC 973): order the points by squared distance to the origin.
  • Queries on Number of Points Inside a Circle (LC 1828): count the points with squared distance at most r * r for each query circle.
  • Count Lattice Points Inside a Circle (LC 2249): test every lattice point near each circle with the squared-distance check.

Manhattan distance

dM=∣x1−x2∣+∣y1−y2∣d_M = |x_1 - x_2| + |y_1 - y_2|

The Manhattan distance is the number of steps on a grid when every step moves one cell up, down, left or right. It is the horizontal difference plus the vertical difference. Use it when a problem defines the cost between two points this way.

Python
Python
1def manhattan(p, q):
2 return abs(p[0] - q[0]) + abs(p[1] - q[1])
3 
4 
5def cells_by_distance(rows, cols, r0, c0):
6 cells = [[r, c] for r in range(rows) for c in range(cols)]
7 cells.sort(key=lambda cell: manhattan(cell, (r0, c0)))
8 return cells
9 
10 
11print(manhattan((3, 12), (-2, 5)))
12print(cells_by_distance(2, 3, 0, 1))
Output
12
[[0, 1], [0, 0], [0, 2], [1, 1], [1, 0], [1, 2]]

LeetCode problems that use it

  • Min Cost to Connect All Points (LC 1584): the cost of an edge between two points is their Manhattan distance.
  • Matrix Cells in Distance Order (LC 1030): sort the cells by Manhattan distance to the given cell.

Chebyshev distance: moves that may go diagonally

dC=max⁡(∣x1−x2∣, ∣y1−y2∣)d_C = \max(|x_1 - x_2|,\ |y_1 - y_2|)

Suppose one move changes x by at most 1 and y by at most 1, so a move can be diagonal. Then the fewest moves between two points is the larger of the two differences. Each diagonal move reduces both differences by 1, and straight moves cover what remains of the larger one.

Python
Python
1def chebyshev(p, q):
2 return max(abs(p[0] - q[0]), abs(p[1] - q[1]))
3 
4 
5def time_to_visit(points):
6 return sum(chebyshev(points[i], points[i + 1]) for i in range(len(points) - 1))
7 
8 
9print(chebyshev((0, 0), (2, 5)))
10print(time_to_visit([[0, 0], [2, 5], [-1, 1]]))
Output
5
9

LeetCode problems that use it

  • Minimum Time Visiting All Points (LC 1266): the time between two consecutive points is their Chebyshev distance.

Largest Manhattan distance: rotate to u = x + y, v = x − y

∣x1−x2∣+∣y1−y2∣=max⁡(∣u1−u2∣, ∣v1−v2∣),u=x+y, v=x−y|x_1 - x_2| + |y_1 - y_2| = \max(|u_1 - u_2|,\ |v_1 - v_2|), \quad u = x + y,\ v = x - y

The substitution turns every Manhattan distance into a Chebyshev distance between the new points (u, v). So the largest Manhattan distance over all pairs is max(max(u) - min(u), max(v) - min(v)). That takes O(n) time instead of checking all n² pairs. The same idea works with more coordinates. A sum of absolute differences equals the largest signed sum over every choice of + and − signs.

Python
Python
1def max_manhattan(points):
2 u = [x + y for x, y in points]
3 v = [x - y for x, y in points]
4 return max(max(u) - min(u), max(v) - min(v))
5 
6 
7print(max_manhattan([[1, 1], [4, 2], [2, 6], [0, 3]]))
Output
6

LeetCode problems that use it

  • Minimize Manhattan Distances (LC 3102): remove one point and find the smallest possible largest distance, using the sorted u and v values.
  • Maximum of Absolute Value Expression (LC 1131): the same identity with three terms, so try each of the sign patterns.

Cross product: which way three points turn

cross⁡(O,A,B)=(Ax−Ox)(By−Oy)−(Ay−Oy)(Bx−Ox)\operatorname{cross}(O, A, B) = (A_x - O_x)(B_y - O_y) - (A_y - O_y)(B_x - O_x)

The sign of cross(O, A, B) tells which way the path from O to A to B turns. A positive value is a counter-clockwise (left) turn. A negative value is a clockwise (right) turn. Zero means the three points are on one straight line. With integer coordinates the result is exact, because there is no division.

Python
Python
1def cross(o, a, b):
2 return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
3 
4 
5print(cross((0, 0), (1, 0), (1, 1)))
6print(cross((0, 0), (1, 0), (1, -1)))
7print(cross((0, 0), (1, 1), (3, 3)))
Output
1
-1
0

LeetCode problems that use it

  • Check If It Is a Straight Line (LC 1232): every point must give a cross product of 0 with the first two points.
  • Valid Boomerang (LC 1037): three points form a boomerang when their cross product is not 0.
  • Convex Polygon (LC 469): a polygon is convex when all its nonzero turns have the same sign.

Card that teaches this method: Cross Product

Slope as an exact dictionary key

key(dx,dy)=(dxg,dyg),g=gcd⁡(∣dx∣,∣dy∣)\text{key}(dx, dy) = \left(\frac{dx}{g}, \frac{dy}{g}\right), \quad g = \gcd(|dx|, |dy|)

Two point pairs have the same slope exactly when their reduced direction (dx, dy) is the same. Divide both differences by their gcd. Then fix the sign so that dx > 0, or dx == 0 and dy > 0. The result is a tuple of integers that you can use as a dictionary key, with no floating-point slope.

Python
Python
1from collections import Counter
2from math import gcd
3 
4 
5def slope_key(p, q):
6 dx, dy = q[0] - p[0], q[1] - p[1]
7 g = gcd(dx, dy)
8 dx, dy = dx // g, dy // g
9 if dx < 0 or (dx == 0 and dy < 0):
10 dx, dy = -dx, -dy
11 return (dx, dy)
12 
13 
14def max_points(points):
15 best = 1
16 for i, p in enumerate(points):
17 counts = Counter(slope_key(p, q) for q in points[i + 1 :])
18 best = max(best, 1 + max(counts.values(), default=0))
19 return best
20 
21 
22print(slope_key((0, 0), (-4, -6)), slope_key((1, 1), (3, 4)))
23print(max_points([[1, 1], [2, 2], [3, 3], [4, 1], [0, 5], [2, 3]]))
Output
(2, 3) (2, 3)
3

LeetCode problems that use it

  • Max Points on a Line (LC 149): for each anchor point, count the other points by slope key.

Card that teaches this method: Canonical Key

Area of a triangle or a polygon: the shoelace formula

A=12∣∑i=0n−1(xi yi+1−xi+1 yi)∣,(xn,yn)=(x0,y0)A = \frac{1}{2}\left|\sum_{i=0}^{n-1} \left(x_i\, y_{i+1} - x_{i+1}\, y_i\right)\right|, \quad (x_n, y_n) = (x_0, y_0)

List the vertices in order around the polygon, either clockwise or counter-clockwise. The sum is twice the signed area. It is positive for counter-clockwise order and negative for clockwise order. For a triangle the formula is half the absolute cross product of its three corners.

Python
Python
1def polygon_area(pts):
2 total = 0
3 for i in range(len(pts)):
4 x1, y1 = pts[i]
5 x2, y2 = pts[(i + 1) % len(pts)]
6 total += x1 * y2 - x2 * y1
7 return abs(total) / 2
8 
9 
10print(polygon_area([(0, 0), (4, 0), (0, 3)]))
11print(polygon_area([(0, 0), (4, 0), (4, 4), (2, 6), (0, 4)]))
Output
6.0
20.0

LeetCode problems that use it

  • Largest Triangle Area (LC 812): try every triple of points and take the largest triangle area.

Card that teaches this method: Cross Product

Convex hull with the monotone chain

while cross⁡(hk−1,hk,p)<0: remove hk\text{while } \operatorname{cross}(h_{k-1}, h_k, p) < 0: \ \text{remove } h_k

Sort the points by (x, y). Build the lower chain from left to right, then the upper chain from right to left. Before adding point p, remove the chain's last point while the chain would turn clockwise at it. The test < 0 keeps points that are on an edge of the hull, which Erect the Fence (LC 587) asks for. The test <= 0 keeps only the corners. Sorting makes the whole method O(n log n).

Python
Python
1def cross(o, a, b):
2 return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
3 
4 
5def outer_trees(trees):
6 pts = sorted(map(tuple, trees))
7 lower, upper = [], []
8 for p in pts:
9 while len(lower) >= 2 and cross(lower[-2], lower[-1], p) < 0:
10 lower.pop()
11 lower.append(p)
12 for p in reversed(pts):
13 while len(upper) >= 2 and cross(upper[-2], upper[-1], p) < 0:
14 upper.pop()
15 upper.append(p)
16 return sorted(set(lower + upper))
17 
18 
19print(outer_trees([[0, 0], [2, 0], [4, 0], [2, 2], [4, 4], [0, 4], [1, 3]]))
Output
[(0, 0), (0, 4), (2, 0), (4, 0), (4, 4)]

LeetCode problems that use it

  • Erect the Fence (LC 587): return every tree on the fence, including trees in the middle of a straight side.

Card that teaches this method: Cross Product

Overlap of two axis-aligned rectangles

w=max⁡(0, min⁡(x2,x4)−max⁡(x1,x3)),h=max⁡(0, min⁡(y2,y4)−max⁡(y1,y3))w = \max(0,\ \min(x_2, x_4) - \max(x_1, x_3)), \quad h = \max(0,\ \min(y_2, y_4) - \max(y_1, y_3))

The first rectangle has bottom-left corner (x1, y1) and top-right corner (x2, y2). The second has corners (x3, y3) and (x4, y4). On each axis, the overlap is the smaller end minus the larger start, or 0 when that is negative. The overlap area is w * h. The area the two rectangles cover together is the sum of their areas minus w * h.

Python
Python
1def overlap(a, b):
2 w = max(0, min(a[2], b[2]) - max(a[0], b[0]))
3 h = max(0, min(a[3], b[3]) - max(a[1], b[1]))
4 return w, h
5 
6 
7def union_area(a, b):
8 w, h = overlap(a, b)
9 area_a = (a[2] - a[0]) * (a[3] - a[1])
10 area_b = (b[2] - b[0]) * (b[3] - b[1])
11 return area_a + area_b - w * h
12 
13 
14print(overlap((0, 0, 4, 3), (2, 1, 6, 5)))
15print(union_area((0, 0, 4, 3), (2, 1, 6, 5)))
16print(overlap((0, 0, 2, 2), (2, 0, 4, 2)))
Output
(2, 2)
24
(0, 2)

LeetCode problems that use it

  • Rectangle Overlap (LC 836): the rectangles overlap only when w > 0 and h > 0, so sharing an edge does not count.
  • Rectangle Area (LC 223): the covered area is the two areas minus the overlap.
  • Find the Largest Area of Square Inside Two Rectangles (LC 3047): the largest square inside an overlap has side min(w, h).

Does a circle touch a rectangle? Clamp the centre

px=min⁡(max⁡(xc,x1),x2),py=min⁡(max⁡(yc,y1),y2),(px−xc)2+(py−yc)2≤r2p_x = \min(\max(x_c, x_1), x_2), \quad p_y = \min(\max(y_c, y_1), y_2), \quad (p_x - x_c)^2 + (p_y - y_c)^2 \le r^2

The point of the rectangle nearest to the centre (xc, yc) is the centre clamped into the rectangle on each axis. The circle and the rectangle share at least one point exactly when that nearest point (px, py) is within distance r of the centre.

Python
Python
1def circle_touches_rect(r, xc, yc, x1, y1, x2, y2):
2 px = min(max(xc, x1), x2)
3 py = min(max(yc, y1), y2)
4 return (px - xc) ** 2 + (py - yc) ** 2 <= r * r
5 
6 
7print(circle_touches_rect(2, 0, 0, 1, 1, 3, 3))
8print(circle_touches_rect(1, 0, 0, 1, 1, 3, 3))
9print(circle_touches_rect(1, 2, 2, 0, 0, 5, 5))
Output
True
False
True

LeetCode problems that use it

  • Circle and Rectangle Overlapping (LC 1401): clamp the centre into the rectangle and compare squared distances.

Turning a direction by 90 degrees

left turn: (dx,dy)→(−dy,dx)right turn: (dx,dy)→(dy,−dx)\text{left turn: } (dx, dy) \to (-dy, dx) \qquad \text{right turn: } (dx, dy) \to (dy, -dx)

Store the facing direction as a vector (dx, dy). A 90-degree turn swaps the two parts and negates one of them, so no angle arithmetic is needed. Four turns in the same direction give back the original vector.

Python
Python
1def is_bounded(instructions):
2 x, y, dx, dy = 0, 0, 0, 1
3 for step in instructions:
4 if step == "G":
5 x, y = x + dx, y + dy
6 elif step == "L":
7 dx, dy = -dy, dx
8 else:
9 dx, dy = dy, -dx
10 return (x, y) == (0, 0) or (dx, dy) != (0, 1)
11 
12 
13print(is_bounded("GR"), is_bounded("GGRGGL"), is_bounded("GLGLGLG"))
Output
True False True

LeetCode problems that use it

  • Robot Bounded In Circle (LC 1041): after one pass the robot stays in a bounded area when it is back at the start or no longer faces north.
  • Spiral Matrix III (LC 885): walk the spiral by turning right with (dr, dc) -> (dc, -dr).

Angle of a point with atan2

θ=atan2⁡(y−y0, x−x0)∈[−180∘,180∘]\theta = \operatorname{atan2}(y - y_0,\ x - x_0) \in [-180^\circ, 180^\circ]

math.atan2(dy, dx) returns the direction from (x0, y0) to (x, y) in radians, and it handles every quadrant and dx == 0. Convert it with math.degrees. To count points inside a viewing angle that may cross the 180-degree boundary, sort the angles and append each angle plus 360. Then, for each angle as the start, use bisect_right to count the angles that are at most the viewing angle after it.

Python
Python
1import math
2from bisect import bisect_right
3 
4 
5def visible_points(points, angle, location):
6 x0, y0 = location
7 at_location = 0
8 angles = []
9 for x, y in points:
10 if (x, y) == (x0, y0):
11 at_location += 1
12 else:
13 angles.append(math.degrees(math.atan2(y - y0, x - x0)))
14 angles.sort()
15 angles += [a + 360 for a in angles]
16 best = 0
17 for i in range(len(angles) // 2):
18 best = max(best, bisect_right(angles, angles[i] + angle + 1e-9) - i)
19 return best + at_location
20 
21 
22print(round(math.degrees(math.atan2(1, -1)), 6))
23print(visible_points([[1, 0], [0, 1], [-1, 0], [0, -1], [1, 1]], 90, [0, 0]))
Output
135.0
3

LeetCode problems that use it

  • Maximum Number of Visible Points (LC 1610): sort the angles, append each plus 360, and count the angles within angle degrees after each start angle.

Angle between the hands of a clock

a=∣30h−5.5m∣(h mod 12),answer=min⁡(a, 360−a)a = |30h - 5.5m| \quad (h \bmod 12), \qquad \text{answer} = \min(a,\ 360 - a)

The minute hand moves 6 degrees per minute. The hour hand moves 30 degrees per hour plus 0.5 degrees per minute. Their difference is 30h + 0.5m - 6m = 30h - 5.5m. The smaller angle between the hands is a or 360 - a, whichever is smaller.

Python
Python
1def clock_angle(hour, minutes):
2 a = abs(30 * (hour % 12) - 5.5 * minutes)
3 return min(a, 360 - a)
4 
5 
6print(clock_angle(6, 0), clock_angle(2, 20), clock_angle(9, 45))
Output
180.0 50.0 22.5

LeetCode problems that use it

  • Angle Between Hands of a Clock (LC 1344): apply the formula and take the smaller of the two angles.

Do four points form a square? Check the six distances

{d2(pi,pj)}i<j={s,s,s,s,2s,2s},s>0\{d^2(p_i, p_j)\}_{i<j} = \{s, s, s, s, 2s, 2s\}, \quad s > 0

Four points have six pairwise distances. They form a square exactly when the six squared distances take only two values, a nonzero side value s that appears four times and a diagonal value 2s that appears twice. Squared distances keep the test in integers.

Python
Python
1from collections import Counter
2from itertools import combinations
3 
4 
5def valid_square(p1, p2, p3, p4):
6 d = Counter(
7 (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2
8 for a, b in combinations([p1, p2, p3, p4], 2)
9 )
10 if len(d) != 2 or 0 in d:
11 return False
12 side, diag = sorted(d)
13 return d[side] == 4 and d[diag] == 2 and diag == 2 * side
14 
15 
16print(valid_square([0, 0], [2, 1], [1, 3], [-1, 2]))
17print(valid_square([0, 0], [2, 0], [2, 1], [0, 1]))
Output
True
False

LeetCode problems that use it

  • Valid Square (LC 593): sort or count the six squared distances.
Area or length as a function of a threshold

Some problems ask for the value h at which a total reaches a target. When the total never decreases as h grows, binary search on h finds it.

Area of squares below a horizontal line

A(h)=∑ili⋅min⁡ ⁣(li, max⁡(0, h−yi))A(h) = \sum_i l_i \cdot \min\!\left(l_i,\ \max(0,\ h - y_i)\right)

Square i has its bottom edge at yi and side li. The part of the square below the line y = h has height h - yi, clamped to the range 0 to li. The function A(h) never decreases as h grows. So a binary search on real numbers finds the lowest h where A(h) reaches half of the total area.

Python
Python
1def area_below(squares, h):
2 return sum(l * min(l, max(0, h - y)) for _, y, l in squares)
3 
4 
5def half_area_line(squares):
6 total = sum(l * l for _, _, l in squares)
7 lo, hi = 0.0, max(y + l for _, y, l in squares)
8 for _ in range(100):
9 mid = (lo + hi) / 2
10 if area_below(squares, mid) * 2 >= total:
11 hi = mid
12 else:
13 lo = mid
14 return hi
15 
16 
17squares = [[0, 0, 2], [5, 1, 3]]
18print(area_below(squares, 2))
19print(round(half_area_line(squares), 5))
Output
7
1.9

LeetCode problems that use it

  • Separate Squares I (LC 3453): find the lowest h where the area below the line is half of the total area. An area covered by several squares counts once for each of them.

Card that teaches this method: Continuous Bisection

How many rounds a binary search on real numbers needs

hi−lo2k≤ε  ⟺  k≥log⁡2hi−loε\frac{hi - lo}{2^k} \le \varepsilon \iff k \ge \log_2 \frac{hi - lo}{\varepsilon}

Each round halves the interval from lo to hi. For hi - lo = 1e9 and a tolerance eps = 1e-5, you need k = 47 rounds, because log⁡2(1014)≈46.5\log_2(10^{14}) \approx 46.5. Prefer a fixed number of rounds to the loop while hi - lo > eps. That loop never ends when eps is smaller than the gap between two neighbouring floats near the answer. In that case mid equals lo or hi, and the interval stops shrinking.

Python
Python
1import math
2 
3lo, hi, eps = 0.0, 1e9, 1e-5
4print(math.ceil(math.log2((hi - lo) / eps)))
5 
6lo, hi = 1e9, 1e9 + 1
7for _ in range(200):
8 mid = (lo + hi) / 2
9 if mid in (lo, hi):
10 break
11 lo = mid
12print(hi - lo, math.ulp(1e9))
Output
47
1.1920928955078125e-07 1.1920928955078125e-07

LeetCode problems that use it

  • Separate Squares I (LC 3453): the answer must be within 10^-5, so a fixed count of rounds is enough.
  • Minimize Max Distance to Gas Station (LC 774): binary-search the smallest possible largest gap D on real numbers.

Card that teaches this method: Continuous Bisection

Integer square root

⌊n⌋=max⁡{x≥0:x2≤n}\lfloor \sqrt{n} \rfloor = \max\{x \ge 0 : x^2 \le n\}

math.isqrt(n) returns this value exactly, for an integer of any size. int(math.sqrt(n)) first converts n to a float, and that can round up for large n. It also solves quadratic limits. For example, the largest k with k(k + 1)/2 <= n is (isqrt(8n + 1) - 1) // 2.

Python
Python
1import math
2 
3n = 10**16 - 1
4print(math.isqrt(n), int(math.sqrt(n)))
5 
6 
7def arrange_coins(n):
8 return (math.isqrt(8 * n + 1) - 1) // 2
9 
10 
11print(arrange_coins(7), arrange_coins(6), arrange_coins(10))
Output
99999999 100000000
3 3 4

LeetCode problems that use it

  • Sqrt(x) (LC 69): binary-search the largest r with r * r <= x. math.isqrt(x) gives the same value, but the problem expects you to compute it yourself.
  • Valid Perfect Square (LC 367): binary-search the largest x with x * x <= num, then check whether x * x == num. The problem does not allow a built-in square root such as math.isqrt.
  • Arranging Coins (LC 441): the number of complete rows is the largest k with k(k + 1)/2 <= n.

Card that teaches this method: Upper Bound

Number theory: divisors, primes and remainders

These formulas work on whole numbers: greatest common divisors, primes, remainders and number bases.

Greatest common divisor with Euclid's algorithm

gcd⁡(a,b)=gcd⁡(b, a mod b),gcd⁡(a,0)=a\gcd(a, b) = \gcd(b,\ a \bmod b), \qquad \gcd(a, 0) = a

Every common divisor of a and b also divides a mod b, so the pair (b, a mod b) has the same gcd. The second number shrinks quickly, so the loop runs O(log min(a, b)) times. Python has math.gcd, and since Python 3.9 it accepts any number of arguments.

Python
Python
1import math
2 
3 
4def gcd(a, b):
5 while b:
6 a, b = b, a % b
7 return a
8 
9 
10print(gcd(84, 36), math.gcd(12, 18, 30))
Output
12 6

LeetCode problems that use it

  • Find Greatest Common Divisor of Array (LC 1979): the gcd of the smallest and the largest value.
  • Greatest Common Divisor of Strings (LC 1071): when str1 + str2 == str2 + str1, the answer is the prefix of length gcd(len(str1), len(str2)).
  • X of a Kind in a Deck of Cards (LC 914): a valid split exists when the gcd of all the value counts is at least 2.
  • Number of Subarrays With GCD Equal to K (LC 2447): extend each subarray to the right while its running gcd is still a multiple of k.
  • Simplified Fractions (LC 1447): a fraction n / d is in lowest terms exactly when gcd(n, d) == 1.

Card that teaches this method: Euclid's GCD

Least common multiple

lcm⁡(a,b)=agcd⁡(a,b)⋅b\operatorname{lcm}(a, b) = \frac{a}{\gcd(a, b)} \cdot b

The lcm is the smallest positive number that both a and b divide. Divide before you multiply, so the intermediate value stays small in languages with fixed-size integers. Python has math.lcm since Python 3.9.

Python
Python
1import math
2 
3 
4def lcm(a, b):
5 return a // math.gcd(a, b) * b
6 
7 
8print(lcm(4, 6), lcm(2, 7), math.lcm(4, 6, 10))
Output
12 14 60

LeetCode problems that use it

  • Smallest Even Multiple (LC 2413): the answer is lcm(2, n).
  • Nth Magical Number (LC 878): numbers divisible by both a and b are the multiples of lcm(a, b).
  • Ugly Number III (LC 1201): the counting formula needs the lcm of every pair and of all three numbers.

Which amounts can two numbers make? Bézout's identity

ax+by=c has integer solutions x,y  ⟺  gcd⁡(a,b) divides ca x + b y = c \text{ has integer solutions } x, y \iff \gcd(a, b) \text{ divides } c

Every integer combination a * x + b * y is a multiple of gcd(a, b). Every multiple of gcd(a, b) can be written that way. For two jugs, the amounts you can measure are the multiples of the gcd that also fit into both jugs together.

Python
Python
1import math
2 
3 
4def can_measure(x, y, target):
5 return target <= x + y and target % math.gcd(x, y) == 0
6 
7 
8print(can_measure(4, 6, 8), can_measure(4, 6, 5), can_measure(3, 7, 11))
Output
True False False

LeetCode problems that use it

  • Water and Jug Problem (LC 365): the target is reachable when it is at most x + y and a multiple of gcd(x, y).
  • Check If It Is a Good Array (LC 1250): some combination of the numbers equals 1 exactly when the gcd of all of them is 1.

All primes below n: the sieve of Eratosthenes

for each prime p≤n: mark p2, p2+p, p2+2p,… as compositeO(nlog⁡log⁡n)\text{for each prime } p \le \sqrt{n}: \text{ mark } p^2,\ p^2 + p,\ p^2 + 2p, \ldots \text{ as composite} \qquad O(n \log \log n)

A composite number below n has a prime factor at most its square root, so marking stops at p * p >= n. Marking starts at p * p because every smaller multiple of p has a smaller prime factor and is already marked.

Python
Python
1def primes_below(n):
2 is_prime = [True] * max(n, 2)
3 is_prime[0] = is_prime[1] = False
4 p = 2
5 while p * p < n:
6 if is_prime[p]:
7 for m in range(p * p, n, p):
8 is_prime[m] = False
9 p += 1
10 return [i for i in range(n) if is_prime[i]]
11 
12 
13print(primes_below(30))
14print(len(primes_below(10**6)))
Output
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
78498

LeetCode problems that use it

  • Count Primes (LC 204): count the primes strictly less than n.
  • Prime Arrangements (LC 1175): with P primes up to n, the answer is P! * (n - P)! modulo 109 + 7.
  • Closest Prime Numbers in Range (LC 2523): sieve up to right, then compare neighbouring primes in the range.

Card that teaches this method: Sieve of Eratosthenes

Smallest-prime-factor sieve: factor many numbers fast

spf[x]=smallest prime dividing x,x=spf[x]⋅xspf[x]\text{spf}[x] = \text{smallest prime dividing } x, \qquad x = \text{spf}[x] \cdot \frac{x}{\text{spf}[x]}

One sieve pass stores the smallest prime factor spf[x] of every x up to a limit. To factor x, divide by spf[x] until x is 1. Each division at least halves x, so factoring takes O(log x) steps after the sieve.

Python
Python
1def build_spf(limit):
2 spf = list(range(limit + 1))
3 p = 2
4 while p * p <= limit:
5 if spf[p] == p:
6 for m in range(p * p, limit + 1, p):
7 if spf[m] == m:
8 spf[m] = p
9 p += 1
10 return spf
11 
12 
13def prime_factors(x, spf):
14 factors = []
15 while x > 1:
16 factors.append(spf[x])
17 x //= spf[x]
18 return factors
19 
20 
21spf = build_spf(100)
22print(prime_factors(84, spf), prime_factors(97, spf))
Output
[2, 2, 3, 7] [97]

LeetCode problems that use it

  • Largest Component Size by Common Factor (LC 952): join every number with its prime factors in a union-find.
  • GCD Sort of an Array (LC 1998): two values can swap when they share a prime factor, so group them by prime factors.

Divisors and prime factors by trial division up to √n

d∣n  ⟹  nd∣n,min⁡ ⁣(d,nd)≤nd \mid n \implies \frac{n}{d} \mid n, \qquad \min\!\left(d, \frac{n}{d}\right) \le \sqrt{n}

Divisors come in pairs (d, n // d), and the smaller one of each pair is at most √n. So a loop over d while d * d <= n finds every divisor in O(√n) time. For prime factors, divide out each d completely. Whatever is left above 1 at the end is one last prime factor.

Python
Python
1def divisors(n):
2 small, large = [], []
3 d = 1
4 while d * d <= n:
5 if n % d == 0:
6 small.append(d)
7 if d != n // d:
8 large.append(n // d)
9 d += 1
10 return small + large[::-1]
11 
12 
13def prime_factors(n):
14 factors = {}
15 d = 2
16 while d * d <= n:
17 while n % d == 0:
18 factors[d] = factors.get(d, 0) + 1
19 n //= d
20 d += 1
21 if n > 1:
22 factors[n] = factors.get(n, 0) + 1
23 return factors
24 
25 
26print(divisors(36))
27print(prime_factors(360))
Output
[1, 2, 3, 4, 6, 9, 12, 18, 36]
{2: 3, 3: 2, 5: 1}

LeetCode problems that use it

  • The kth Factor of n (LC 1492): list the divisors in increasing order and return the k-th one.
  • Four Divisors (LC 1390): stop a number as soon as it has more than four divisors.
  • Distinct Prime Factors of Product of Array (LC 2521): collect the distinct prime factors of every number.
  • Smallest Value After Replacing With Sum of Prime Factors (LC 2507): replace n by the sum of its prime factors until it stops changing.

Only perfect squares have an odd number of divisors

#{d:d∣n} is odd  ⟺  n=k2\#\{d : d \mid n\} \text{ is odd} \iff n = k^2

Divisors of n come in pairs (d, n // d). The two members of a pair are different unless d * d == n. So the count is odd exactly when n is a perfect square. Between 1 and n there are isqrt(n) perfect squares.

Python
Python
1import math
2 
3 
4def bulbs_on(n):
5 return math.isqrt(n)
6 
7 
8print([d for d in range(1, 17) if 16 % d == 0])
9print(bulbs_on(10), bulbs_on(100))
Output
[1, 2, 4, 8, 16]
3 10

LeetCode problems that use it

  • Bulb Switcher (LC 319): bulb i is toggled once per divisor of i, so it ends on when i is a perfect square.

Arithmetic modulo 109+710^9 + 7

(a+b) mod M=((a mod M)+(b mod M)) mod M,(a⋅b) mod M=((a mod M)(b mod M)) mod M(a + b) \bmod M = ((a \bmod M) + (b \bmod M)) \bmod M, \qquad (a \cdot b) \bmod M = ((a \bmod M)(b \bmod M)) \bmod M

When a problem asks for the answer modulo M = 109 + 7, reduce after every addition and multiplication. Python integers never overflow, but a very large integer makes each operation slower. Division does not follow these rules. It needs the modular inverse, described in the entry on dividing modulo a prime. In Python, a % M is always in the range 0 to M - 1 for a positive M, even when a is negative. C++ and Java return a negative remainder for a negative a.

Python
Python
1MOD = 10**9 + 7
2 
3 
4def dice_ways(n, k, target):
5 ways = [1] + [0] * target
6 for _ in range(n):
7 new = [0] * (target + 1)
8 for total in range(target + 1):
9 if ways[total]:
10 for face in range(1, k + 1):
11 if total + face <= target:
12 new[total + face] = (new[total + face] + ways[total]) % MOD
13 ways = new
14 return ways[target]
15 
16 
17print(-7 % 5, -7 // 5)
18print(dice_ways(3, 6, 10), dice_ways(20, 20, 200))
Output
3 -2
27 270319085

LeetCode problems that use it

  • Number of Dice Rolls With Target Sum (LC 1155): reduce every sum of ways modulo 109 + 7.
  • Concatenation of Consecutive Binary Numbers (LC 1680): shift the result left by the bit length of each new number, then reduce.
  • Count Good Numbers (LC 1922): multiply the powers of 5 and 4 modulo 109 + 7.

Subarray sums divisible by k: equal prefix remainders

∑t=ij−1at=Pj−Pi≡0(modk)  ⟺  Pi mod k=Pj mod k\sum_{t=i}^{j-1} a_t = P_j - P_i \equiv 0 \pmod{k} \iff P_i \bmod k = P_j \bmod k

P[j] is the sum of the first j values, with P[0] = 0. A subarray sum is a difference of two prefix sums. It is divisible by k exactly when the two prefix sums have the same remainder. So count equal remainders in a dictionary while you scan the array.

Python
Python
1from collections import Counter
2 
3 
4def subarrays_div_by_k(nums, k):
5 seen = Counter({0: 1})
6 prefix = count = 0
7 for x in nums:
8 prefix = (prefix + x) % k
9 count += seen[prefix]
10 seen[prefix] += 1
11 return count
12 
13 
14print(subarrays_div_by_k([3, 1, 2, -3, 6], 3))
Output
10

LeetCode problems that use it

  • Subarray Sums Divisible by K (LC 974): count the pairs of equal prefix remainders.
  • Continuous Subarray Sum (LC 523): store the first index of each remainder, and require a length of at least 2.

Card that teaches this method: Prefix Sum Map

Fast power by repeated squaring

an={(an/2)2n evena⋅an−1n odda^n = \begin{cases} \left(a^{n/2}\right)^2 & n \text{ even} \\ a \cdot a^{n-1} & n \text{ odd} \end{cases}

Read the bits of n from the lowest. Square the base once per bit, and multiply the result by the base when the bit is 1. That takes O(log n) multiplications instead of n. Python's pow(a, n, m) does the same with a modulus.

Python
Python
1def power(a, n, mod):
2 result = 1
3 a %= mod
4 while n:
5 if n & 1:
6 result = result * a % mod
7 a = a * a % mod
8 n >>= 1
9 return result
10 
11 
12print(power(3, 13, 1000), pow(3, 13, 1000))
13print(power(2, 10**18, 10**9 + 7))
Output
323 323
719476260

LeetCode problems that use it

  • Pow(x, n) (LC 50): square the base and halve the exponent, and for a negative n use 1 / x as the base.
  • Super Pow (LC 372): the exponent is given as a digit list, so combine a^(10 * e + d) = (a^e)^10 * a^d.
  • Count Good Numbers (LC 1922): the answer is 5^ceil(n/2) * 4^floor(n/2) modulo 109 + 7.

Card that teaches this method: Fast Power

Dividing modulo a prime: the modular inverse

a−1≡aM−2(modM)for prime M that does not divide a,(nk)≡n!⋅(k!)−1⋅((n−k)!)−1(n<M)a^{-1} \equiv a^{M-2} \pmod{M} \quad \text{for prime } M \text{ that does not divide } a, \qquad \binom{n}{k} \equiv n! \cdot (k!)^{-1} \cdot ((n-k)!)^{-1} \quad (n < M)

Fermat's little theorem gives a^(M-1) ≡ 1 modulo a prime M, so a^(M-2) is the inverse of a. Multiplying by it replaces division. Since Python 3.8, pow(a, -1, M) computes the inverse directly. Precompute factorials and inverse factorials once, and every binomial coefficient costs O(1).

Python
Python
1MOD = 10**9 + 7
2N = 1000
3fact = [1] * (N + 1)
4for i in range(1, N + 1):
5 fact[i] = fact[i - 1] * i % MOD
6inv_fact = [1] * (N + 1)
7inv_fact[N] = pow(fact[N], MOD - 2, MOD)
8for i in range(N, 0, -1):
9 inv_fact[i - 1] = inv_fact[i] * i % MOD
10 
11 
12def comb_mod(n, k):
13 if k < 0 or k > n:
14 return 0
15 return fact[n] * inv_fact[k] % MOD * inv_fact[n - k] % MOD
16 
17 
18print(pow(3, MOD - 2, MOD) * 3 % MOD, pow(3, -1, MOD) == pow(3, MOD - 2, MOD))
19print(comb_mod(10, 3), comb_mod(1000, 500))
Output
1 True
120 159835829

LeetCode problems that use it

  • Number of Ways to Reach a Position After Exactly k Steps (LC 2400): with distance d = |endPos - startPos|, the answer is C(k, (k + d) / 2) when k + d is even and d <= k.
  • Count Ways to Build Rooms in an Ant Colony (LC 1916): the count is n! divided by the product of all subtree sizes, so divide with inverses.

Digital root: repeated digit sum

dr⁡(n)={0n=01+(n−1) mod 9n>0\operatorname{dr}(n) = \begin{cases} 0 & n = 0 \\ 1 + (n - 1) \bmod 9 & n > 0 \end{cases}

A number and its digit sum have the same remainder modulo 9, because 10 ≡ 1 modulo 9. Summing digits until one digit remains therefore gives the remainder modulo 9, with 9 in place of 0 for a positive n.

Python
Python
1def digital_root(n):
2 return 0 if n == 0 else 1 + (n - 1) % 9
3 
4 
5print(digital_root(47), digital_root(99999), digital_root(0))
Output
2 9 0

LeetCode problems that use it

  • Add Digits (LC 258): return the digital root in O(1) time.

Converting a number to another base

n=∑idi bi,(n,d0)=(⌊n/b⌋, n mod b) repeatedn = \sum_i d_i\, b^i, \qquad (n, d_0) = \left(\left\lfloor n / b \right\rfloor,\ n \bmod b\right) \text{ repeated}

Repeated divmod(n, b) produces the digits from the least significant one, so reverse them at the end. For a negative base such as −2, a remainder can come out negative. Then add |b| to the remainder and add 1 to the quotient. For a negative number in hexadecimal as a 32-bit value, first take n & 0xFFFFFFFF, which is its two's complement.

Python
Python
1DIGITS = "0123456789abcdef"
2 
3 
4def to_base(n, b):
5 if n == 0:
6 return "0"
7 sign, n, out = "-" if n < 0 else "", abs(n), []
8 while n:
9 n, d = divmod(n, b)
10 out.append(DIGITS[d])
11 return sign + "".join(reversed(out))
12 
13 
14def to_base_neg2(n):
15 out = []
16 while n:
17 n, d = divmod(n, -2)
18 if d < 0:
19 n, d = n + 1, d + 2
20 out.append(str(d))
21 return "".join(reversed(out)) or "0"
22 
23 
24print(to_base(50, 7), to_base(-20, 7))
25print(to_base(-2 & 0xFFFFFFFF, 16), to_base(255, 16))
26print(to_base_neg2(6), to_base_neg2(7))
Output
101 -26
fffffffe ff
11010 11011

LeetCode problems that use it

  • Base 7 (LC 504): repeated divmod by 7, with the sign added back.
  • Convert a Number to Hexadecimal (LC 405): convert num & 0xFFFFFFFF so a negative number becomes its two's complement.
  • Convert to Base -2 (LC 1017): divide by −2 and fix every negative remainder.

Excel column titles: base 26 with digits 1 to 26

n=∑ici⋅26i,ci∈{1,…,26}title: (n,c)=divmod⁡(n−1,26)n = \sum_i c_i \cdot 26^i, \quad c_i \in \{1, \ldots, 26\} \qquad \text{title: } (n, c) = \operatorname{divmod}(n - 1, 26)

Column titles have no zero digit. A is 1 and Z is 26. So subtract 1 before each divmod(n, 26), and read the remainder as 0 for A up to 25 for Z. For the reverse direction, n = n * 26 + (letter value) for each letter from the left.

Python
Python
1def column_title(n):
2 out = []
3 while n:
4 n, r = divmod(n - 1, 26)
5 out.append(chr(ord("A") + r))
6 return "".join(reversed(out))
7 
8 
9def column_number(title):
10 n = 0
11 for ch in title:
12 n = n * 26 + (ord(ch) - ord("A") + 1)
13 return n
14 
15 
16print(column_title(26), column_title(27), column_title(702), column_title(703))
17print(column_number("AZ"), column_number("BA"))
Output
Z AA ZZ AAA
52 53

LeetCode problems that use it

  • Excel Sheet Column Title (LC 168): subtract 1 before each division by 26.
  • Excel Sheet Column Number (LC 171): read the title as a base-26 number with digits 1 to 26.

Trailing zeros of n!

Z(n)=∑k≥1⌊n5k⌋Z(n) = \sum_{k \ge 1} \left\lfloor \frac{n}{5^k} \right\rfloor

Each trailing zero needs one factor 10, which is 2 × 5. Factors of 2 are more common than factors of 5 in n!. So the count of zeros is the count of factors 5 in n!. Each multiple of 5 gives one factor, each multiple of 25 gives one more, and so on.

Python
Python
1def trailing_zeros(n):
2 count = 0
3 while n:
4 n //= 5
5 count += n
6 return count
7 
8 
9print(trailing_zeros(25), trailing_zeros(100), trailing_zeros(10**9))
Output
6 24 249999998

LeetCode problems that use it

  • Factorial Trailing Zeroes (LC 172): sum n // 5 + n // 25 + ....
  • Preimage Size of Factorial Zeroes Function (LC 793): Z(n) never decreases, so binary-search the first n with Z(n) >= k. The answer is always 0 or 5.

Staying inside the 32-bit integer range

[−231, 231−1]=[−2147483648, 2147483647][-2^{31},\ 2^{31} - 1] = [-2147483648,\ 2147483647]

Python integers never overflow. So when a problem describes a 32-bit environment, you must compare with the range yourself. Division is the other difference. Python's // rounds down, while C++, Java and many problem statements truncate toward zero. For truncation, divide the absolute values and apply the sign.

Python
Python
1INT_MIN, INT_MAX = -(2**31), 2**31 - 1
2 
3 
4def clamp32(x):
5 return max(INT_MIN, min(INT_MAX, x))
6 
7 
8def trunc_div(a, b):
9 q = abs(a) // abs(b)
10 return q if (a < 0) == (b < 0) else -q
11 
12 
13print(INT_MIN, INT_MAX)
14print(clamp32(2**40), -7 // 2, trunc_div(-7, 2))
Output
-2147483648 2147483647
2147483647 -4 -3

LeetCode problems that use it

  • Reverse Integer (LC 7): return 0 when the reversed number is outside the range.
  • String to Integer (atoi) (LC 8): clamp the parsed value to the range.
  • Divide Two Integers (LC 29): truncate toward zero. The only result outside the range is -231 / -1. The problem does not allow //, so build the quotient from subtractions of shifted divisors.

Card that teaches this method: Digit Manipulation

Combinatorics: counting without listing

These formulas count arrangements, selections and pairs directly, so a solution never has to list them one by one.

Binomial coefficient: choose k of n

(nk)=n!k! (n−k)!\binom{n}{k} = \frac{n!}{k!\,(n-k)!}

C(n, k) counts the ways to choose k items from n when order does not matter. Python's math.comb(n, k) computes it exactly. A grid path with r moves down and c moves right is one choice of which r of the r + c moves go down.

Python
Python
1import math
2 
3 
4def unique_paths(m, n):
5 return math.comb(m + n - 2, m - 1)
6 
7 
8print(math.comb(5, 2), unique_paths(4, 5), unique_paths(10, 10))
Output
10 35 48620

LeetCode problems that use it

  • Unique Paths (LC 62): the answer is C(m + n - 2, m - 1).
  • Number of Ways to Reach a Position After Exactly k Steps (LC 2400): choose which of the k steps go right.

Pascal's rule: binomial coefficients by addition

(nk)=(n−1k−1)+(n−1k),(n0)=(nn)=1\binom{n}{k} = \binom{n-1}{k-1} + \binom{n-1}{k}, \qquad \binom{n}{0} = \binom{n}{n} = 1

Either the n-th item is chosen, and k - 1 more come from the other n - 1, or it is not chosen, and all k come from the other n - 1. The rule builds a table of all C(i, j) with additions only. So it also works under any modulus, prime or not, with no division.

Python
Python
1def pascal_rows(num_rows):
2 rows = [[1]]
3 for _ in range(num_rows - 1):
4 prev = rows[-1]
5 rows.append([1] + [prev[j - 1] + prev[j] for j in range(1, len(prev))] + [1])
6 return rows
7 
8 
9print(pascal_rows(6))
Output
[[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1], [1, 5, 10, 10, 5, 1]]

LeetCode problems that use it

  • Pascal's Triangle (LC 118): build each row from the previous one.
  • Pascal's Triangle II (LC 119): keep only one row and update it from right to left.
  • Number of Ways to Reorder Array to Get Same BST (LC 1569): the left and right subtree orders interleave in C(left + right, left) ways.

Card that teaches this method: Grid DP

Catalan numbers

Cn=1n+1(2nn),C0=1,  Cn+1=∑i=0nCi Cn−iC_n = \frac{1}{n+1}\binom{2n}{n}, \qquad C_0 = 1,\ \ C_{n+1} = \sum_{i=0}^{n} C_i\, C_{n-i}

Cn counts three things of size n. It counts the binary search trees with n keys and the balanced strings of n pairs of parentheses. It also counts the ways to join 2n points on a circle with n chords that do not cross. The sum form fixes the root of the tree, or the position of the ) that matches the first (. Then i items go on one side and n - i on the other. The sequence starts 1, 1, 2, 5, 14, 42.

Python
Python
1import math
2 
3 
4def catalan(n):
5 return math.comb(2 * n, n) // (n + 1)
6 
7 
8def catalan_dp(n):
9 c = [1] + [0] * n
10 for m in range(1, n + 1):
11 c[m] = sum(c[i] * c[m - 1 - i] for i in range(m))
12 return c[n]
13 
14 
15print([catalan(n) for n in range(8)])
16print(catalan_dp(19))
Output
[1, 1, 2, 5, 14, 42, 132, 429]
1767263190

LeetCode problems that use it

  • Unique Binary Search Trees (LC 96): the number of binary search trees with keys 1 to n is Cn.
  • Generate Parentheses (LC 22): the formula does not build the strings, but the output has Cn strings, so any solution takes at least Cn steps, which bounds its running time.
  • Handshakes That Don't Cross (LC 1259): for numPeople people the answer is C_(numPeople / 2) modulo 109 + 7.

Stars and bars: splitting n equal items into k groups

#{(x1,…,xk):xi≥0, ∑xi=n}=(n+k−1k−1)\#\{(x_1, \ldots, x_k) : x_i \ge 0,\ \textstyle\sum x_i = n\} = \binom{n + k - 1}{k - 1}

Write the n items as stars and put k - 1 bars between them to separate the k groups. Every arrangement of n stars and k - 1 bars in a row is one split. So the count is the number of ways to place the bars among n + k - 1 positions.

Python
Python
1import math
2 
3 
4def splits(n, k):
5 return math.comb(n + k - 1, k - 1)
6 
7 
8def sorted_vowel_strings(n):
9 return splits(n, 5)
10 
11 
12print(splits(4, 3), sorted_vowel_strings(3), sorted_vowel_strings(10))
Output
15 35 1001

LeetCode problems that use it

  • Count Sorted Vowel Strings (LC 1641): a sorted string is fixed by how many of each of the 5 vowels it uses: C(n + 4, 4).
  • Count Ways to Make Array With Product (LC 1735): for each prime power p^e in k, spread the e factors over the n positions, then multiply.

Arrangements of a multiset

n!c1! c2!⋯cm!,c1+⋯+cm=n\frac{n!}{c_1!\, c_2! \cdots c_m!}, \qquad c_1 + \cdots + c_m = n

n items where value i appears ci times have this many different orders. The n! orders count each arrangement once for every way of reordering equal values among themselves. Dividing by each ci! removes those repeats.

Python
Python
1import math
2from collections import Counter
3from itertools import permutations
4 
5 
6def arrangements(word):
7 result = math.factorial(len(word))
8 for c in Counter(word).values():
9 result //= math.factorial(c)
10 return result
11 
12 
13print(arrangements("aabbb"), len(set(permutations("aabbb"))))
14print(arrangements("mississippi"))
Output
10 10
34650

LeetCode problems that use it

  • Count Anagrams (LC 2514): multiply the arrangement counts of all the words, modulo 109 + 7.
  • Permutations II (LC 47): the formula does not build the permutations, but the output has exactly this many of them, which bounds the running time.

The k-th permutation: the factorial number system

k−1=∑i=1nqi (n−i)!,0≤qi≤n−ik - 1 = \sum_{i=1}^{n} q_i\,(n - i)!, \quad 0 \le q_i \le n - i

The permutations that start with the same first item form a block of (n - 1)! permutations. So (k - 1) // (n - 1)! is the index of the first item among the unused items. Keep the remainder and repeat with (n - 2)! for the next position.

Python
Python
1import math
2 
3 
4def kth_permutation(n, k):
5 items = list(range(1, n + 1))
6 k -= 1
7 out = []
8 for i in range(n, 0, -1):
9 q, k = divmod(k, math.factorial(i - 1))
10 out.append(str(items.pop(q)))
11 return "".join(out)
12 
13 
14print(kth_permutation(3, 1), kth_permutation(3, 4), kth_permutation(4, 24))
Output
123 231 4321

LeetCode problems that use it

  • Permutation Sequence (LC 60): pick each digit with divmod(k - 1, (n - 1)!).

Card that teaches this method: Count and Skip

Inclusion–exclusion

∣A∪B∣=∣A∣+∣B∣−∣A∩B∣,∣A∪B∪C∣=∑∣A∣−∑∣A∩B∣+∣A∩B∩C∣|A \cup B| = |A| + |B| - |A \cap B|, \qquad |A \cup B \cup C| = \sum|A| - \sum|A \cap B| + |A \cap B \cap C|

Adding the sizes of the sets counts every element of an intersection more than once. Subtract the pairwise intersections, then add back the triple intersection. For example, the numbers from 1 to x divisible by a or b number x // a + x // b - x // lcm(a, b). With an upper limit per group, stars and bars plus inclusion–exclusion subtracts the splits where some group is over the limit.

Python
Python
1import math
2 
3 
4def count_div(x, a, b):
5 return x // a + x // b - x // math.lcm(a, b)
6 
7 
8def distribute(n, limit):
9 total = 0
10 for i in range(4):
11 rest = n - i * (limit + 1)
12 if rest >= 0:
13 total += (-1) ** i * math.comb(3, i) * math.comb(rest + 2, 2)
14 return total
15 
16 
17print(count_div(30, 4, 6))
18print(distribute(6, 3), distribute(10, 4))
Output
10
10 6

LeetCode problems that use it

  • Nth Magical Number (LC 878): binary-search the smallest x with count_div(x, a, b) >= n.
  • Ugly Number III (LC 1201): the same search with three numbers and seven terms.
  • Distribute Candies Among Children II (LC 2929): count all splits of n among 3 children, then subtract those where a child gets more than limit.
  • Rectangle Area (LC 223): the covered area is the two areas minus their overlap.

Counting pairs of equal items

(c2)=c(c−1)2\binom{c}{2} = \frac{c(c-1)}{2}

A group of c equal items holds c(c - 1)/2 unordered pairs. So group the items by a key, then add this value for every group. This takes O(n) time instead of comparing all pairs.

Python
Python
1from collections import Counter
2 
3 
4def good_pairs(nums):
5 return sum(c * (c - 1) // 2 for c in Counter(nums).values())
6 
7 
8print(good_pairs([2, 7, 2, 2, 7]), good_pairs([5, 5, 5, 5]))
Output
4 6

LeetCode problems that use it

  • Number of Good Pairs (LC 1512): count the pairs inside each group of equal values.
  • Number of Pairs of Interchangeable Rectangles (LC 2001): group the rectangles by their reduced width-to-height ratio.
  • Count Nice Pairs in an Array (LC 1814): group the numbers by nums[i] - rev(nums[i]).

How many subarrays contain index i

#{[l,r]:l≤i≤r}=(i+1)(n−i),∑subarrays∑a=∑i=0n−1ai (i+1)(n−i)\#\{[l, r] : l \le i \le r\} = (i + 1)(n - i), \qquad \sum_{\text{subarrays}} \sum a = \sum_{i=0}^{n-1} a_i\,(i + 1)(n - i)

A subarray that contains index i starts at one of the i + 1 indices from 0 to i. It ends at one of the n - i indices from i to n - 1. So instead of listing all subarrays, add each element's value times the number of subarrays that contain it.

Python
Python
1def sum_of_subarray_sums(nums):
2 n = len(nums)
3 return sum(x * (i + 1) * (n - i) for i, x in enumerate(nums))
4 
5 
6def vowels_in_substrings(word):
7 n = len(word)
8 return sum((i + 1) * (n - i) for i, ch in enumerate(word) if ch in "aeiou")
9 
10 
11print(sum_of_subarray_sums([1, 2, 3]), vowels_in_substrings("code"))
Output
20 10

LeetCode problems that use it

  • Vowels of All Substrings (LC 2063): each vowel at index i is in (i + 1)(n - i) substrings.
  • Sum of Subarray Minimums (LC 907): replace the two factors with the counts of subarrays where arr[i] is the minimum.
  • Count Unique Characters of All Substrings of a Given String (LC 828): each character counts in the substrings that contain no other copy of it.

Card that teaches this method: Contribution Technique

Series and sums

A closed formula for a sum replaces a loop, and it often turns a search over values into one equation.

Sum 1 + 2 + … + n and other arithmetic series

∑i=1ni=n(n+1)2,a+(a+1)+⋯+(a+k−1)=k a+k(k−1)2\sum_{i=1}^{n} i = \frac{n(n+1)}{2}, \qquad a + (a+1) + \cdots + (a+k-1) = k\,a + \frac{k(k-1)}{2}

Pair the first term with the last, the second with the second-to-last, and so on. Every pair has the same sum, n + 1. The second formula is the sum of k consecutive integers that start at a. It tells you which k and a can produce a given total.

Python
Python
1def missing_number(nums):
2 n = len(nums)
3 return n * (n + 1) // 2 - sum(nums)
4 
5 
6def consecutive_sums(n):
7 count, k = 0, 1
8 while k * (k + 1) // 2 <= n:
9 if (n - k * (k - 1) // 2) % k == 0:
10 count += 1
11 k += 1
12 return count
13 
14 
15print(missing_number([3, 0, 1, 4]), consecutive_sums(21), consecutive_sums(32))
Output
2 4 1

LeetCode problems that use it

  • Missing Number (LC 268): the missing value is n(n + 1)/2 - sum(nums).
  • Find the Pivot Integer (LC 2485): the pivot x satisfies x * x == n(n + 1)/2.
  • Consecutive Numbers Sum (LC 829): for each length k, check that n - k(k - 1)/2 is a positive multiple of k.
  • Find Three Consecutive Integers That Sum to a Given Number (LC 2177): three consecutive integers sum to 3x + 3, so num must be a multiple of 3.

Sum of squares

∑i=1ni2=n(n+1)(2n+1)6\sum_{i=1}^{n} i^2 = \frac{n(n+1)(2n+1)}{6}

Use it when a total grows with the square of a size. With the plain sum, it also gives two equations for two unknown numbers. If dup appears twice and miss is missing from 1 to n, the plain sums give dup - miss and the square sums give dup² - miss². Dividing the second by the first gives dup + miss.

Python
Python
1def sum_squares(n):
2 return n * (n + 1) * (2 * n + 1) // 6
3 
4 
5def find_error_nums(nums):
6 n = len(nums)
7 diff = sum(nums) - n * (n + 1) // 2
8 diff_sq = sum(x * x for x in nums) - sum_squares(n)
9 total = diff_sq // diff
10 dup = (diff + total) // 2
11 return [dup, dup - diff]
12 
13 
14print(sum_squares(10), find_error_nums([1, 3, 3, 4, 5]), find_error_nums([2, 2]))
Output
385 [3, 2] [2, 1]

LeetCode problems that use it

  • Set Mismatch (LC 645): solve the two equations for the duplicate and the missing number.
  • Minimum Garden Perimeter to Collect Enough Apples (LC 1954): the square from (-k, -k) to (k, k) holds 2k(k + 1)(2k + 1) apples.

Triangular and tetrahedral numbers

Tk=k(k+1)2,∑j=1kTj=k(k+1)(k+2)6T_k = \frac{k(k+1)}{2}, \qquad \sum_{j=1}^{k} T_j = \frac{k(k+1)(k+2)}{6}

Tk is the number of boxes in a triangle with side k. Stacking the triangles with sides 1 to k gives a pyramid of k(k + 1)(k + 2)/6 boxes, with Tk boxes on the floor.

Python
Python
1def tetrahedral(k):
2 return k * (k + 1) * (k + 2) // 6
3 
4 
5print([tetrahedral(k) for k in range(1, 7)])
6print(sum(j * (j + 1) // 2 for j in range(1, 7)))
Output
[1, 4, 10, 20, 35, 56]
56

LeetCode problems that use it

  • Building Boxes (LC 1739): find the largest full pyramid with at most n boxes, then add floor boxes one at a time.

Geometric series

1+r+r2+⋯+rm=rm+1−1r−1,r≠11 + r + r^2 + \cdots + r^m = \frac{r^{m+1} - 1}{r - 1}, \quad r ≠ 1

A number whose digits in base k are all 1 equals 1 + k + ... + k^m. For a fixed number of digits m + 1, this sum grows with k, and k is about the m-th root of n. So each digit count has at most one candidate base to check.

Python
Python
1def smallest_good_base(n):
2 for digits in range(n.bit_length(), 1, -1):
3 k = int(n ** (1 / (digits - 1)))
4 for base in (k - 1, k, k + 1):
5 if base >= 2 and (base**digits - 1) // (base - 1) == n:
6 return base
7 return n - 1
8 
9 
10print((3**5 - 1) // (3 - 1), sum(3**i for i in range(5)))
11print(smallest_good_base(31), smallest_good_base(43), smallest_good_base(10**12 + 1))
Output
121 121
2 6 1000000000000

LeetCode problems that use it

  • Smallest Good Base (LC 483): for each digit count from the largest down, test the one candidate base.

Harmonic sum: loops over all multiples cost O(n log n)

∑i=1n⌊ni⌋≤n∑i=1n1i≈nln⁡n\sum_{i=1}^{n} \left\lfloor \frac{n}{i} \right\rfloor \le n \sum_{i=1}^{n} \frac{1}{i} \approx n \ln n

A double loop that visits every multiple of every i from 1 to n runs n // i steps for each i. The total is about n ln n, not n². This is why a pass over all multiples of all numbers up to 105 is fast enough.

Python
Python
1import math
2 
3n = 10**5
4steps = sum(n // i for i in range(1, n + 1))
5print(steps, round(n * math.log(n)))
Output
1166750 1151293

LeetCode problems that use it

  • Graph Connectivity With Threshold (LC 1627): join every z > threshold with all of its multiples.

Josephus recurrence

J(1,k)=0,J(n,k)=(J(n−1,k)+k) mod nJ(1, k) = 0, \qquad J(n, k) = (J(n-1, k) + k) \bmod n

n people stand in a circle, numbered 0 to n - 1, and every k-th person leaves. J(n, k) is the 0-based position of the last person left. After the first removal, the circle of n - 1 people is the same problem shifted by k positions. So the recurrence runs in O(n) time with no simulation.

Python
Python
1def last_remaining(n, k):
2 pos = 0
3 for size in range(2, n + 1):
4 pos = (pos + k) % size
5 return pos
6 
7 
8print(last_remaining(7, 3) + 1, last_remaining(10, 2) + 1, last_remaining(1, 5) + 1)
Output
4 5 1

LeetCode problems that use it

  • Find the Winner of the Circular Game (LC 1823): the winner is J(n, k) + 1 with 1-based numbering.

Card that teaches this method: Josephus Problem

Probability and randomization

These entries compute exact probabilities, or produce random choices that are exactly uniform.

Probability over equally likely moves

P(s)=∑m=1M1M P(next(s,m))P(s) = \sum_{m=1}^{M} \frac{1}{M}\, P(\text{next}(s, m))

When every one of M moves has the same chance, the probability of success from state s is the average over the moves. Fill a table over states and remaining moves, like any other dynamic programming table.

Python
Python
1def knight_probability(n, k, row, col):
2 moves = [(1, 2), (2, 1), (2, -1), (1, -2), (-1, -2), (-2, -1), (-2, 1), (-1, 2)]
3 prob = [[1.0] * n for _ in range(n)]
4 for _ in range(k):
5 new = [[0.0] * n for _ in range(n)]
6 for r in range(n):
7 for c in range(n):
8 for dr, dc in moves:
9 if 0 <= r + dr < n and 0 <= c + dc < n:
10 new[r][c] += prob[r + dr][c + dc] / 8
11 prob = new
12 return prob[row][col]
13 
14 
15print(knight_probability(4, 1, 1, 1), knight_probability(5, 2, 2, 2))
Output
0.5 0.375

LeetCode problems that use it

  • Knight Probability in Chessboard (LC 688): average the probability of staying on the board over the 8 knight moves.
  • New 21 Game (LC 837): average over the maxPts card values, with a sliding-window sum.
  • Soup Servings (LC 808): average over the 4 serving choices.

Reservoir sampling: one uniform pick from a stream

P(item j kept)=1j∏i=j+1n(1−1i)=1j⋅jn=1nP(\text{item } j \text{ kept}) = \frac{1}{j} \prod_{i=j+1}^{n} \left(1 - \frac{1}{i}\right) = \frac{1}{j} \cdot \frac{j}{n} = \frac{1}{n}

Replace the kept item with the i-th item (1-based) with probability 1/i. Item j is kept at step j, and then survives every later step i with probability (i - 1)/i. The product telescopes, so every item ends with probability 1/n, and you never need to know n in advance.

Python
Python
1import random
2from fractions import Fraction
3 
4 
5def reservoir_pick(stream):
6 chosen = None
7 for i, item in enumerate(stream, start=1):
8 if random.randrange(i) == 0:
9 chosen = item
10 return chosen
11 
12 
13def exact_keep_probability(j, n):
14 p = Fraction(1, j)
15 for i in range(j + 1, n + 1):
16 p *= Fraction(i - 1, i)
17 return p
18 
19 
20print([str(exact_keep_probability(j, 5)) for j in range(1, 6)])
Output
['1/5', '1/5', '1/5', '1/5', '1/5']

LeetCode problems that use it

  • Linked List Random Node (LC 382): pick a random node of a linked list of unknown length.
  • Random Pick Index (LC 398): pick a random index among the positions that hold the target.

Card that teaches this method: Reservoir Sampling

Weighted random choice with prefix sums

pick r∈{1,…,W} uniformly,i=min⁡{i:Pi≥r},Pi=w0+⋯+wi\text{pick } r \in \{1, \ldots, W\} \text{ uniformly}, \quad i = \min\{i : P_i \ge r\}, \qquad P_i = w_0 + \cdots + w_i

Lay the weights end to end on a line of length W, the total weight. Index i owns wi of the W positions. A uniform r from 1 to W, searched with bisect_left in the prefix sums, lands in index i with probability wi / W.

Python
Python
1import random
2from bisect import bisect_left
3from collections import Counter
4from itertools import accumulate
5 
6 
7def pick_index(prefix, r):
8 return bisect_left(prefix, r)
9 
10 
11weights = [1, 3, 2]
12prefix = list(accumulate(weights))
13print(prefix, pick_index(prefix, random.randint(1, prefix[-1])) in range(3))
14print(Counter(pick_index(prefix, r) for r in range(1, prefix[-1] + 1)))
Output
[1, 4, 6] True
Counter({1: 3, 2: 2, 0: 1})

LeetCode problems that use it

  • Random Pick with Weight (LC 528): search a uniform random number in the prefix sums.
  • Random Point in Non-overlapping Rectangles (LC 497): weight each rectangle by its number of integer points, (x2 - x1 + 1) * (y2 - y1 + 1).

Card that teaches this method: Lower Bound

Fisher–Yates shuffle

for i=n−1,…,1: j←uniform{0,…,i},  swap(ai,aj)n⋅(n−1)⋯2=n!\text{for } i = n-1, \ldots, 1: \ j \leftarrow \text{uniform}\{0, \ldots, i\},\ \ \text{swap}(a_i, a_j) \qquad n \cdot (n-1) \cdots 2 = n!

Each step picks the item for position i uniformly from the items not yet placed. The choices multiply to n! equally likely sequences, and each sequence gives a different permutation. So every permutation has probability 1/n!. Picking j from the whole array at every step is a known mistake. It gives n^n equally likely sequences. For n >= 3 that number is not a multiple of n!, so the permutations cannot all be equally likely.

Python
Python
1import random
2from itertools import product
3 
4 
5def shuffle(a):
6 for i in range(len(a) - 1, 0, -1):
7 j = random.randint(0, i)
8 a[i], a[j] = a[j], a[i]
9 return a
10 
11 
12def all_outcomes(n):
13 results = []
14 for choices in product(*[range(i + 1) for i in range(n - 1, 0, -1)]):
15 a = list(range(n))
16 for i, j in zip(range(n - 1, 0, -1), choices):
17 a[i], a[j] = a[j], a[i]
18 results.append(tuple(a))
19 return results
20 
21 
22outcomes = all_outcomes(4)
23print(len(outcomes), len(set(outcomes)), sorted(shuffle([1, 2, 3])))
Output
24 24 [1, 2, 3]

LeetCode problems that use it

  • Shuffle an Array (LC 384): shuffle a copy of the array with the Fisher–Yates loop.
  • Random Flip Matrix (LC 519): the same swap trick on a virtual array, stored in a dictionary.

Rejection sampling

v=7(a−1)+b∈{1,…,49},accept v≤40,E[rounds]=4940v = 7(a - 1) + b \in \{1, \ldots, 49\}, \quad \text{accept } v \le 40, \qquad E[\text{rounds}] = \frac{49}{40}

Two calls of a uniform 1-to-7 generator give 49 equally likely values v. Keep v only when it is at most 40, and return (v - 1) % 10 + 1. Each result from 1 to 10 then comes from exactly 4 of the 40 kept values. A round succeeds with probability p = 40/49, so the expected number of rounds is 1/p = 49/40.

Python
Python
1from collections import Counter
2 
3 
4def rand10_from(a, b):
5 v = 7 * (a - 1) + b
6 return (v - 1) % 10 + 1 if v <= 40 else None
7 
8 
9results = Counter(rand10_from(a, b) for a in range(1, 8) for b in range(1, 8))
10print(results[None], sorted(set(results.values()) - {results[None]}))
Output
9 [4]

LeetCode problems that use it

  • Implement Rand10() Using Rand7() (LC 470): combine two calls into 49 values and reject the last 9.
  • Generate Random Point in a Circle (LC 478): draw points in the square around the circle and keep the ones inside.

Games decided by a simple rule

win(s)=∃ m:¬ win(next(s,m)),Nim with take 1..3: win(n)  ⟺  n mod 4≠0\text{win}(s) = \exists\, m : \lnot\, \text{win}(\text{next}(s, m)), \qquad \text{Nim with take } 1..3: \ \text{win}(n) \iff n \bmod 4 ≠ 0

A position is winning when some move leads to a losing position for the opponent. Some games have a short answer. In the Nim game with 1 to 3 stones per turn, you lose exactly when n is a multiple of 4. If you take t stones, the opponent takes 4 - t, and n stays a multiple of 4. Compute the game table for small inputs first, and look for the pattern.

Python
Python
1def winners(limit, moves):
2 win = [False] * (limit + 1)
3 for n in range(1, limit + 1):
4 win[n] = any(not win[n - m] for m in moves(n) if m <= n)
5 return win
6 
7 
8nim = winners(12, lambda n: [1, 2, 3])
9divisor = winners(12, lambda n: [x for x in range(1, n) if n % x == 0])
10print([n for n in range(1, 13) if not nim[n]])
11print([n for n in range(1, 13) if divisor[n]])
Output
[4, 8, 12]
[2, 4, 6, 8, 10, 12]

LeetCode problems that use it

  • Nim Game (LC 292): you win when n % 4 != 0.
  • Divisor Game (LC 1025): the first player wins when n is even.
  • Stone Game (LC 877): the number of piles is even and the total is odd. The first player can take all the even-indexed piles or all the odd-indexed piles. One of these two sets has the larger sum, so the first player always wins.

Card that teaches this method: Minimax DP

Bit math

These identities work on the binary form of integers. In Python a negative integer behaves as if it had infinitely many leading 1 bits.

Powers of two, four and three

n>0 and n&(n−1)=0  ⟺  n=2kn > 0 \text{ and } n \mathbin{\&} (n - 1) = 0 \iff n = 2^k

n - 1 flips the lowest 1 bit of n and every 0 bit below it. So n & (n - 1) clears the lowest 1 bit, and the result is 0 only when n had one 1 bit. A power of four is a power of two whose 1 bit is at an even position, so n & 0x55555555 != 0. For three there is no bit test. Inside the 32-bit range, 319 = 1162261467 is the largest power of three, so n is a power of three when n > 0 and 1162261467 % n == 0.

Python
Python
1def is_power_of_two(n):
2 return n > 0 and n & (n - 1) == 0
3 
4 
5def is_power_of_four(n):
6 return is_power_of_two(n) and n & 0x55555555 != 0
7 
8 
9def is_power_of_three(n):
10 return n > 0 and 3**19 % n == 0
11 
12 
13print(is_power_of_two(64), is_power_of_two(96))
14print(is_power_of_four(64), is_power_of_four(32))
15print(3**19, is_power_of_three(243), is_power_of_three(45))
Output
True False
True False
1162261467 True False

LeetCode problems that use it

  • Power of Two (LC 231): test n & (n - 1) == 0 for a positive n.
  • Power of Four (LC 342): add the even-position mask 0x55555555.
  • Power of Three (LC 326): check that n divides 319.

Card that teaches this method: Kernighan's Bit Trick

Lowest set bit

x&(−x)=lowest 1 bit of x,x&(x−1)=x without its lowest 1 bitx \mathbin{\&} (-x) = \text{lowest 1 bit of } x, \qquad x \mathbin{\&} (x - 1) = x \text{ without its lowest 1 bit}

In two's complement, -x equals ~x + 1. Below the lowest 1 bit of x, x has only 0 bits, so ~x has only 1 bits there. Adding 1 turns those 1 bits into 0 and sets the lowest 1 bit of x. Above that bit, -x is ~x, so it differs from x in every bit. So x & -x keeps exactly one bit, the lowest 1 bit of x.

Python
Python
1def single_number_iii(nums):
2 xor_all = 0
3 for x in nums:
4 xor_all ^= x
5 low = xor_all & -xor_all
6 a = 0
7 for x in nums:
8 if x & low:
9 a ^= x
10 return sorted([a, xor_all ^ a])
11 
12 
13print(12 & -12, 12 & 11, single_number_iii([4, 7, 4, 9, 2, 2]))
Output
4 8 [7, 9]

LeetCode problems that use it

  • Single Number III (LC 260): split the numbers by the lowest bit where the two answers differ.
  • Range Sum Query - Mutable (LC 307): a Fenwick tree moves between indices with i += i & -i and i -= i & -i.

Card that teaches this method: Fenwick Tree

XOR cancels equal values

a⊕a=0,a⊕0=a,al⊕⋯⊕ar=Pr+1⊕Pla \oplus a = 0, \quad a \oplus 0 = a, \qquad a_l \oplus \cdots \oplus a_r = P_{r+1} \oplus P_l

XOR is commutative and associative, and a value XORed with itself is 0. So in the XOR of a list, every value that appears twice cancels. P[i] is the XOR of the first i values. The XOR of a range is the XOR of two prefix values, exactly like a range sum.

Python
Python
1from functools import reduce
2from operator import xor
3 
4 
5def single_number(nums):
6 return reduce(xor, nums)
7 
8 
9def range_xor(nums, queries):
10 prefix = [0]
11 for x in nums:
12 prefix.append(prefix[-1] ^ x)
13 return [prefix[r + 1] ^ prefix[l] for l, r in queries]
14 
15 
16print(single_number([6, 3, 6, 8, 3]))
17print(range_xor([5, 2, 7, 1], [[0, 1], [1, 3], [2, 2]]))
Output
8
[7, 4, 7]

LeetCode problems that use it

  • Single Number (LC 136): XOR all values, and the pairs cancel.
  • Missing Number (LC 268): XOR the values with every index from 0 to n.
  • XOR Queries of a Subarray (LC 1310): answer each query with two prefix XOR values.
  • Find The Original Array of Prefix Xor (LC 2433): arr[i] = pref[i] ^ pref[i - 1].

Card that teaches this method: XOR Cancel

Counting 1 bits

popcount⁡(x)=popcount⁡(x≫1)+(x&1)\operatorname{popcount}(x) = \operatorname{popcount}(x \gg 1) + (x \mathbin{\&} 1)

The loop x &= x - 1 removes one 1 bit per round, so it runs once per 1 bit. The recurrence fills a table of counts for 0 to n in O(n) time. Python 3.10 and newer also have x.bit_count(). bin(x).count("1") works in every version.

Python
Python
1def popcount(x):
2 count = 0
3 while x:
4 x &= x - 1
5 count += 1
6 return count
7 
8 
9def count_bits(n):
10 bits = [0] * (n + 1)
11 for i in range(1, n + 1):
12 bits[i] = bits[i >> 1] + (i & 1)
13 return bits
14 
15 
16print(popcount(13), bin(13).count("1"), count_bits(8), popcount(3 ^ 12))
Output
3 3 [0, 1, 1, 2, 1, 2, 2, 3, 1] 4

LeetCode problems that use it

  • Number of 1 Bits (LC 191): remove one 1 bit per round.
  • Counting Bits (LC 338): fill the table with bits[i >> 1] + (i & 1).
  • Hamming Distance (LC 461): the Hamming distance is the popcount of x ^ y.

Card that teaches this method: Kernighan's Bit Trick

Adding two integers with bit operations

a+b=(a⊕b)+((a&b)≪1)a + b = (a \oplus b) + ((a \mathbin{\&} b) \ll 1)

a ^ b adds each bit position without carries. (a & b) << 1 holds the carries, moved one position left. Repeat with these two numbers until the carry is 0. Python integers have no fixed width, so keep 32 bits with a mask and convert the result back to a signed value at the end.

Python
Python
1MASK = 0xFFFFFFFF
2 
3 
4def get_sum(a, b):
5 while b & MASK:
6 a, b = (a ^ b) & MASK, ((a & b) << 1) & MASK
7 a &= MASK
8 return a if a <= 0x7FFFFFFF else ~(a ^ MASK)
9 
10 
11print(get_sum(7, 8), get_sum(-5, 3), get_sum(-8, -9))
Output
15 -2 -17

LeetCode problems that use it

  • Sum of Two Integers (LC 371): repeat the XOR and carry steps inside a 32-bit mask.
Logarithms and Big-O math

Logarithms count how many times a size can be halved or multiplied. They set the cost of binary search and of divide-and-conquer.

Logarithms, digit counts and bit lengths

log⁡bx=ln⁡xln⁡b,digits10(n)=⌊log⁡10n⌋+1,bits(n)=⌊log⁡2n⌋+1\log_b x = \frac{\ln x}{\ln b}, \qquad \text{digits}_{10}(n) = \lfloor \log_{10} n \rfloor + 1, \qquad \text{bits}(n) = \lfloor \log_2 n \rfloor + 1

A floating-point logarithm can be slightly wrong, so never use it to test whether a number is an exact power. math.log(243, 3) returns a value just below 5. For counts, use integer tools. len(str(n)) counts decimal digits and n.bit_length() counts bits.

Python
Python
1import math
2 
3print(math.log(243, 3), 3**5 == 243)
4n = 1000
5print(len(str(n)), n.bit_length(), math.floor(math.log2(n)) + 1)
Output
4.999999999999999 True
4 10 10

LeetCode problems that use it

  • Power of Three (LC 326): multiply or divide by 3 with integers instead of taking a logarithm.
  • Concatenation of Consecutive Binary Numbers (LC 1680): shift the running value left by i.bit_length() before adding i.

Master theorem for divide and conquer

T(n)=a T(n/b)+O(nd)  ⟹  T(n)={O(nd)d>log⁡baO(ndlog⁡n)d=log⁡baO(nlog⁡ba)d<log⁡baT(n) = a\,T(n/b) + O(n^d) \implies T(n) = \begin{cases} O(n^d) & d > \log_b a \\ O(n^d \log n) & d = \log_b a \\ O(n^{\log_b a}) & d < \log_b a \end{cases}

a is the number of recursive calls, n/b the size of each call, and O(n^d) the work outside the calls. Compare d with log_b a to see whether the top level, every level equally, or the leaves dominate the total work.

Python
Python
1from functools import lru_cache
2 
3 
4@lru_cache(maxsize=None)
5def merge_sort_work(n):
6 return n if n <= 1 else 2 * merge_sort_work(n // 2) + n
7 
8 
9@lru_cache(maxsize=None)
10def fast_power_steps(n):
11 return 1 if n <= 1 else fast_power_steps(n // 2) + 1
12 
13 
14n = 2**20
15print(merge_sort_work(n) == n * 20 + n, fast_power_steps(n))
Output
True 21

LeetCode problems that use it

  • Pow(x, n) (LC 50): T(n) = T(n/2) + O(1), so a = 1, b = 2, d = 0, and the cost is O(log n).
  • Sort an Array (LC 912): merge sort has T(n) = 2T(n/2) + O(n), so the cost is O(n log n).

How many probes a binary search makes

probes(N)≤⌊log⁡2N⌋+1=⌈log⁡2(N+1)⌉\text{probes}(N) \le \lfloor \log_2 N \rfloor + 1 = \lceil \log_2 (N + 1) \rceil

Each probe of a binary search over N sorted items discards the middle item and at least half of the rest. So the search makes at most N.bit_length() probes. For N = 106 that is 20 probes.

Python
Python
1def probes(nums, target):
2 lo, hi, count = 0, len(nums) - 1, 0
3 while lo <= hi:
4 mid = (lo + hi) // 2
5 count += 1
6 if nums[mid] == target:
7 break
8 if nums[mid] < target:
9 lo = mid + 1
10 else:
11 hi = mid - 1
12 return count
13 
14 
15nums = list(range(1000))
16print(max(probes(nums, t) for t in range(-1, 1001)), len(nums).bit_length())
17print((10**6).bit_length())
Output
10 10
20

LeetCode problems that use it

  • Binary Search (LC 704): the search makes at most len(nums).bit_length() probes.

Card that teaches this method: Exact Match

Matrix index math

These formulas map positions in a grid with m rows and n columns to other positions, so a transformation needs no extra search.

Transpose and rotate a matrix

transpose: (i,j)→(j,i)rotate clockwise: (i,j)→(j, n−1−i)\text{transpose: } (i, j) \to (j, i) \qquad \text{rotate clockwise: } (i, j) \to (j,\ n - 1 - i)

Rotating a square matrix clockwise moves the value at row i, column j to row j, column n - 1 - i. In place, that is a transpose followed by reversing every row. A transpose of a matrix that is not square changes its shape to n rows and m columns, so build a new matrix with zip(*matrix).

Python
Python
1def rotate(matrix):
2 n = len(matrix)
3 for i in range(n):
4 for j in range(i + 1, n):
5 matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
6 for row in matrix:
7 row.reverse()
8 return matrix
9 
10 
11print(rotate([[1, 2], [3, 4]]))
12print([list(col) for col in zip(*[[1, 2], [3, 4], [5, 6]])])
Output
[[3, 1], [4, 2]]
[[1, 3, 5], [2, 4, 6]]

LeetCode problems that use it

  • Rotate Image (LC 48): transpose in place, then reverse every row.
  • Transpose Matrix (LC 867): build the n by m result with zip(*matrix).
  • Determine Whether Matrix Can Be Obtained By Rotation (LC 1886): compare the target with the matrix after 0, 1, 2 and 3 rotations.

Spiral order with four boundaries

layers=⌈min⁡(m,n)2⌉\text{layers} = \left\lceil \frac{\min(m, n)}{2} \right\rceil

Keep four boundaries, top, bottom, left and right. Walk the top row, the right column, the bottom row and the left column. After each side, move its boundary one step inward. Check the boundaries again before the bottom row and the left column, because a single remaining row or column must not be walked twice. The loop runs once per ring, and a grid with m rows and n columns has ⌈min(m, n)/2⌉ rings.

Python
Python
1def spiral_order(matrix):
2 top, bottom, left, right = 0, len(matrix) - 1, 0, len(matrix[0]) - 1
3 out = []
4 while top <= bottom and left <= right:
5 out += [matrix[top][c] for c in range(left, right + 1)]
6 top += 1
7 out += [matrix[r][right] for r in range(top, bottom + 1)]
8 right -= 1
9 if top <= bottom:
10 out += [matrix[bottom][c] for c in range(right, left - 1, -1)]
11 bottom -= 1
12 if left <= right:
13 out += [matrix[r][left] for r in range(bottom, top - 1, -1)]
14 left += 1
15 return out
16 
17 
18print(spiral_order([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]))
Output
[1, 2, 3, 6, 9, 12, 11, 10, 7, 4, 5, 8]

LeetCode problems that use it

  • Spiral Matrix (LC 54): read the values layer by layer.
  • Spiral Matrix II (LC 59): write 1 to n * n along the same path.
  • Spiral Matrix IV (LC 2326): write the linked-list values along the same path.

Card that teaches this method: Spiral Matrix

Diagonals: r − c and r + c are constant

same ↘ diagonal  ⟺  r1−c1=r2−c2,same ↙ diagonal  ⟺  r1+c1=r2+c2\text{same } \searrow \text{ diagonal} \iff r_1 - c_1 = r_2 - c_2, \qquad \text{same } \swarrow \text{ diagonal} \iff r_1 + c_1 = r_2 + c_2

Moving one step down and one step right keeps r - c the same. Moving one step down and one step left keeps r + c the same. So either value is a key that groups the cells of a diagonal. An m by n grid has m + n - 1 diagonals in each direction.

Python
Python
1from collections import defaultdict
2 
3 
4def group_diagonals(mat):
5 groups = defaultdict(list)
6 for r, row in enumerate(mat):
7 for c, value in enumerate(row):
8 groups[r - c].append(value)
9 return dict(groups)
10 
11 
12def is_toeplitz(mat):
13 return len(group_diagonals(mat)) == sum(
14 len(set(v)) for v in group_diagonals(mat).values()
15 )
16 
17 
18print(group_diagonals([[1, 2, 3], [4, 5, 6]]))
19print(is_toeplitz([[1, 2, 3], [4, 1, 2], [5, 4, 1]]), is_toeplitz([[1, 2], [3, 3]]))
Output
{0: [1, 5], -1: [2, 6], -2: [3], 1: [4]}
True False

LeetCode problems that use it

  • Diagonal Traverse (LC 498): walk the anti-diagonals r + c = 0, 1, ..., reversing every other one.
  • Sort the Matrix Diagonally (LC 1329): sort each group of cells with the same r - c.
  • Toeplitz Matrix (LC 766): every group with the same r - c holds one value.
  • N-Queens (LC 51): a queen blocks its row, its column, its r - c diagonal and its r + c diagonal.

Card that teaches this method: Canonical Key

Flatten a 2D index into one number

k=r⋅n+c,(r,c)=divmod⁡(k,n)k = r \cdot n + c, \qquad (r, c) = \operatorname{divmod}(k, n)

Reading an m by n grid row by row gives cell (r, c) the position k = r * n + c. divmod(k, n) converts back. This lets a binary search, a reshape or a random pick treat the grid as one list of m * n cells.

Python
Python
1def search_matrix(matrix, target):
2 m, n = len(matrix), len(matrix[0])
3 lo, hi = 0, m * n - 1
4 while lo <= hi:
5 mid = (lo + hi) // 2
6 r, c = divmod(mid, n)
7 if matrix[r][c] == target:
8 return True
9 if matrix[r][c] < target:
10 lo = mid + 1
11 else:
12 hi = mid - 1
13 return False
14 
15 
16grid = [[2, 4, 6], [8, 10, 12], [14, 16, 18]]
17print(divmod(7, 3), search_matrix(grid, 10), search_matrix(grid, 11))
Output
(2, 1) True False

LeetCode problems that use it

  • Search a 2D Matrix (LC 74): binary-search the flattened index from 0 to m * n - 1.
  • Reshape the Matrix (LC 566): copy value k of the old shape to divmod(k, c) of the new shape.
  • Random Flip Matrix (LC 519): pick a random flattened index, then convert it to a cell.

Card that teaches this method: Exact Match

Remainders, ranges and exact comparisons

Short facts that decide whether a loop ends, how many values a range holds, and how to compare fractions without rounding.

Remainders repeat within k steps

rt+1=f(rt) mod k  ⟹  among r0,…,rk two values are equalr_{t+1} = f(r_t) \bmod k \implies \text{among } r_0, \ldots, r_k \text{ two values are equal}

There are only k possible remainders modulo k. So among k + 1 remainders, two are equal (the pigeonhole principle). When the next remainder depends only on the current one, the sequence repeats from that point. So a search over remainders can stop after k steps.

Python
Python
1def smallest_repunit_div_by_k(k):
2 r = 0
3 for length in range(1, k + 1):
4 r = (r * 10 + 1) % k
5 if r == 0:
6 return length
7 return -1
8 
9 
10print(smallest_repunit_div_by_k(7), smallest_repunit_div_by_k(41))
11print(smallest_repunit_div_by_k(10))
Output
6 5
-1

LeetCode problems that use it

  • Smallest Integer Divisible by K (LC 1015): track (r * 10 + 1) % k. If no remainder is 0 within k steps, the answer is −1.
  • Fraction to Recurring Decimal (LC 166): the decimal digits start to repeat when a remainder repeats.

The smallest missing positive is between 1 and n + 1

min⁡(Z>0∖{a0,…,an−1})∈[1,n+1]\min\left(\mathbb{Z}_{>0} \setminus \{a_0, \ldots, a_{n-1}\}\right) \in [1, n + 1]

n numbers can cover at most the n values 1 to n. So the answer is one of 1 to n + 1. Because only the values 1 to n matter, the array itself can serve as the table. Place each value v from 1 to n at index v - 1. Then the answer is i + 1 for the first index i where nums[i] != i + 1.

Python
Python
1def first_missing_positive(nums):
2 n = len(nums)
3 for i in range(n):
4 while 1 <= nums[i] <= n and nums[nums[i] - 1] != nums[i]:
5 j = nums[i] - 1
6 nums[i], nums[j] = nums[j], nums[i]
7 for i in range(n):
8 if nums[i] != i + 1:
9 return i + 1
10 return n + 1
11 
12 
13print(first_missing_positive([2, 5, -3, 1, 7]), first_missing_positive([1, 2, 3]))
Output
3 4

LeetCode problems that use it

  • First Missing Positive (LC 41): place every value from 1 to n at its own index.
  • Find All Numbers Disappeared in an Array (LC 448): the missing values are the indices that do not hold their value.

Card that teaches this method: Cyclic Sort

Long multiplication of digit strings

digit i of a×digit j of b → position i+j+1 of a result with m+n digits\text{digit } i \text{ of } a \times \text{digit } j \text{ of } b \ \to \ \text{position } i + j + 1 \text{ of a result with } m + n \text{ digits}

Number the digits from the left, starting at 0. A product of an m-digit and an n-digit number has at most m + n digits. The product of digit i and digit j adds to position i + j + 1, and its carry goes to position i + j.

Python
Python
1def multiply(a, b):
2 res = [0] * (len(a) + len(b))
3 for i in range(len(a) - 1, -1, -1):
4 for j in range(len(b) - 1, -1, -1):
5 total = res[i + j + 1] + int(a[i]) * int(b[j])
6 res[i + j + 1] = total % 10
7 res[i + j] += total // 10
8 return "".join(map(str, res)).lstrip("0") or "0"
9 
10 
11print(multiply("123", "45"), multiply("99", "99"), multiply("0", "52"))
Output
5535 9801 0

LeetCode problems that use it

  • Multiply Strings (LC 43): fill the m + n digit array, then remove leading zeros.

Counting in a range, and rounding a division up

#{x∈[lo,hi]:k∣x}=⌊hik⌋−⌊lo−1k⌋,⌈ab⌉=⌊a+b−1b⌋,splits(n,L)=⌈nL⌉−1=⌊n−1L⌋\#\{x \in [lo, hi] : k \mid x\} = \left\lfloor \frac{hi}{k} \right\rfloor - \left\lfloor \frac{lo - 1}{k} \right\rfloor, \qquad \left\lceil \frac{a}{b} \right\rceil = \left\lfloor \frac{a + b - 1}{b} \right\rfloor, \qquad \text{splits}(n, L) = \left\lceil \frac{n}{L} \right\rceil - 1 = \left\lfloor \frac{n - 1}{L} \right\rfloor

The multiples of k from 1 to hi number hi // k. Subtract the ones below lo. The odd numbers from low to high number (high + 1) // 2 - low // 2. For a positive b, round a division up with (a + b - 1) // b or -(-a // b). Both stay in integers, unlike math.ceil(a / b), which goes through a float. Splitting a pile of n into pieces no larger than L takes one fewer split than the ceil(n / L) pieces that produces, and substituting the ceiling-division identity above and canceling the Ls collapses ceil(n / L) - 1 down to the single floor division (n - 1) // L.

Python
Python
1def count_multiples(lo, hi, k):
2 return hi // k - (lo - 1) // k
3 
4 
5def count_odds(low, high):
6 return (high + 1) // 2 - low // 2
7 
8 
9def ceil_div(a, b):
10 return -(-a // b)
11 
12 
13def splits_needed(n, limit):
14 return (n - 1) // limit
15 
16 
17print(count_multiples(10, 30, 7), count_odds(4, 11), count_odds(5, 5))
18print(ceil_div(7, 3), ceil_div(6, 3), ceil_div(10**18 + 1, 1))
19print(splits_needed(9, 3), splits_needed(9, 2))
Output
3 4 1
3 2 1000000000000000001
2 4

LeetCode problems that use it

  • Count Odd Numbers in an Interval Range (LC 1523): the count is (high + 1) // 2 - low // 2.
  • Koko Eating Bananas (LC 875): a pile of p bananas takes ceil(p / k) hours.
  • Minimum Speed to Arrive on Time (LC 1870): each ride except the last takes a whole number of hours, rounded up.
  • Minimum Limit of Balls in a Bag (LC 1760): splitting a bag of n balls down to a limit of L takes (n - 1) // L operations.

Card that teaches this method: Feasible Function

Comparing fractions exactly

ab=cd  ⟺  a d=c b(b,d≠0),ab<cd  ⟺  a d<c b(b,d>0)\frac{a}{b} = \frac{c}{d} \iff a\,d = c\,b \quad (b, d ≠ 0), \qquad \frac{a}{b} < \frac{c}{d} \iff a\,d < c\,b \quad (b, d > 0)

Two different fractions with large numerators and denominators can round to the same float. Cross-multiplying keeps the comparison in integers, which Python computes exactly. fractions.Fraction does the same and also reduces the fraction.

Python
Python
1from fractions import Fraction
2 
3a, b = 999999999, 999999998
4c, d = 999999998, 999999997
5print(a / b == c / d, a * d == c * b)
6print(Fraction(a, b) == Fraction(c, d), Fraction(6, 8))
Output
True False
False 3/4

LeetCode problems that use it

  • Minimum Lines to Represent a Line Chart (LC 2280): compare the slopes of neighbouring segments by cross-multiplying.
  • K-th Smallest Prime Fraction (LC 786): keep the largest fraction arr[i - 1] / arr[j] found so far by testing arr[i - 1] * best[1] > best[0] * arr[j]. Both denominators are positive, so the direction of > is kept.