Number and Math
What is this?
Three problems that each have an obvious slow solution and a short correct one. Testing every number up to n for primality is quadratic-ish; crossing off multiples is nearly linear. Checking every triple for the largest product is cubic; sorting once makes it two comparisons. Testing whether a number's only prime factors are 2, 3 and 5 by factorising it fully is unnecessary; dividing those factors out and checking what is left is three loops.
The pattern is the same each time: stop asking about every candidate and start eliminating whole classes at once.
💡 Fun fact: the Sieve of Eratosthenes is around 2,200 years old and still the practical way to list primes below a few million. Two refinements make it fast: start crossing off at
p × p— every smaller multiple ofpalready has a smaller prime factor and was struck out earlier — and stop the outer loop at√n, because any composite belownhas a factor no larger than that. Those two details take it fromO(n log n)-ish toO(n log log n).
🔓 The 4 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
The one-line idea: eliminate classes rather than testing individuals — divide known factors out, cross off multiples in bulk, or sort so that only two candidate answers remain to compare.
1. Divide the factors out
def is_ugly(n):
if n <= 0:
return False
for p in (2, 3, 5):
while n % p == 0:
n //= p
return n == 1
If dividing away every 2, 3 and 5 leaves exactly 1, the number had no other prime factors. The n <= 0 guard matters: zero would loop forever and negatives are excluded by definition.
2. Sieve, do not test
def count_primes(n):
if n < 3:
return 0
is_prime = [True] * n
is_prime[0] = is_prime[1] = False
p = 2
while p * p < n:
if is_prime[p]:
for multiple in range(p * p, n, p): # start at p*p, not 2p
is_prime[multiple] = False
p += 1
return sum(is_prime)
Starting at p × p is not a micro-optimisation — every multiple k × p with k < p has a prime factor smaller than p and was already crossed off when that smaller prime was processed.
3. Enumerate the sign cases
The largest product of three numbers is one of exactly two candidates:
- the three largest values, or
- the two most negative values times the largest.
The second wins when two large negatives multiply into a large positive. Sorting makes both readable in one line:
nums.sort()
return max(nums[-1] * nums[-2] * nums[-3],
nums[0] * nums[1] * nums[-1])
There is a linear alternative that tracks the three largest and two smallest in one pass — worth mentioning as the O(n) improvement once the sorted version is on the board.
4. A 30-second worked example
nums = [-10, -10, 1, 3, 2]
sorted: [-10, -10, 1, 2, 3]
candidate A — three largest: 1 x 2 x 3 = 6
candidate B — two smallest x max: -10 x -10 x 3 = 300 ✅
answer = 300
Taking only the three largest gives 6 and is wrong. The two negatives cancel, which is exactly the case the second candidate exists to catch.
5. Where you'll actually meet this
- Cryptography and hashing. Prime generation underpins key selection and table sizing.
- Numeric libraries. Smooth-number tests (only small prime factors) decide whether a fast Fourier transform size is efficient.
- Financial and scoring calculations. Extreme-product selections where negative values are meaningful.
- Load and shard sizing. Choosing sizes with convenient factorisations.
- Competitive and simulation code. Precomputed prime tables are a standard warm-up step.
6. Problems in this chapter
▶ Ugly Number
Decide whether a number's only prime factors are 2, 3 and 5. Divide each out repeatedly and check that 1 remains.
Pattern: factor elimination. Target: O(log n) time, O(1) space.
▶ Count Primes
Count primes below n. Sieve of Eratosthenes, crossing off from p × p with the outer loop bounded by √n.
Pattern: sieve. Target: O(n log log n) time, O(n) space.
▶ Maximum Product of Three Numbers
Largest product of any three values. Sort, then take the better of the top three and the two smallest times the largest.
Pattern: sign-case enumeration. Target: O(n log n) sorted, or O(n) with a single tracking pass.
7. Common pitfalls 🚫
- Looping forever on zero in the factor test. Guard
n <= 0first. - Starting the sieve at
2p. Correct but slower;p × pis the right lower bound. - Running the outer sieve loop to
n. It only needs to reach√n. - Off-by-one on "below n". Count Primes excludes
nitself, so the array hasnentries indexed0..n-1. - Taking only the three largest for the product. Two negatives can beat them.
- Assuming at least one positive value exists. An all-negative array still has a well-defined answer.
- Overflow. Three values near 10³ are fine, but in fixed-width languages the general case needs a 64-bit type.
- Day of the Week — the weekday for a given date. Either count days since a known anchor or apply Zeller's congruence; the leap-year rule (divisible by 4, except centuries, unless divisible by 400) is the part that must be exactly right.
8. Key takeaways
- Eliminate classes, not individuals. Cross off multiples; divide out factors.
p × pand√nare the two details that make a sieve fast.- Two candidates decide the extreme product, and the negative case is the one that gets missed.
- Sort for clarity, then offer the linear version — showing both is stronger than either alone.
- Guard the degenerate inputs. Zero, negatives and tiny
nare where these fail. - Know the standard complexities.
O(n log log n)for a sieve is worth saying precisely. - Why interviewers like it: short problems with well-known optimal solutions, so they reveal quickly whether you know the standard tools or are improvising.
Order: Ugly Number → Count Primes → Maximum Product of Three Numbers → Day of the Week.
Day of the Week
Core idea: A weekday repeats every 7 days, forever. So if you know one anchor — say, January 1, 1971 was a Friday — then the weekday of any other date is just
(number of days between them) mod 7, stepped forward from that anchor. The entire problem collapses to one question: how many days lie between the anchor and the target date? Everything hard about this problem — leap years — lives inside that single count.
Problem, rephrased
You're building the recurrence engine of a scheduling service — "every second Tuesday," payroll on business days — and it must label any calendar date with its weekday without calling a date library. You're given a date as three integers: a day, a month, and a year, with the year guaranteed to lie between 1971 and 2100.
Return the name of the weekday that date falls on — one of "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday". There's nothing to look up: you must count your way there from a date whose weekday you already know — and the count has to survive leap years, because every February 29 you miscount drifts every later answer by one weekday.
Strip the story away and this is Day of the Week: given day, month, and year, return the weekday name of that date.
Input (day, month, year) |
Output | Why |
|---|---|---|
31, 8, 2019 |
"Saturday" |
LeetCode's own sample |
18, 7, 1999 |
"Sunday" |
LeetCode's own sample |
15, 8, 1993 |
"Sunday" |
LeetCode's own sample |
1, 1, 1971 |
"Friday" |
the anchor date itself — zero days elapsed |
29, 2, 2000 |
"Tuesday" |
a real leap day (2000 is divisible by 400) |
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson