Hi👋SpeedAlgo • Deliberate Practice & Cognitive Ergonomics for Software Engineers

An interactive algorithm mastery and technical interview preparation platform published by Hi👋WebEnterprise. Built for senior and staff software engineers preparing for rigorous coding screens at top tech companies (FAANG/MAMAA).

13 Core Algorithmic Patterns & 225 Practice Problems

  • 1. Two Pointers (16 Paradigms, 43 Problems): Opposite ends, sorted pair sums, palindrome verification, trapping rain water, 3Sum, container with most water — includes the Sliding Window and Fast & Slow Pointers paradigms (contiguous subarray invariants, longest substrings, Floyd's cycle detection, linked list middle).
  • 2. Binary Search (9 Paradigms, 14 Problems): Monotonic predicate partitioning (feasible(x)), boundary search, rotated sorted arrays, square roots, minimum ship capacity, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 11 Problems): Counting set bits, XOR cancellation, bit reversal, bitmask pairing, maximum XOR with a bitwise trie.
  • 4. Math & Geometry (8 Paradigms, 14 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (15 Paradigms, 24 Problems): Path sums, lowest common ancestor, tree diameter, validating BSTs, tree DP (House Robber III, Binary Tree Cameras).
  • 6. Tree/Graph Breadth-First Search (5 Paradigms, 12 Problems): Level-order traversals, shortest path in unweighted graphs, zig-zag traversals, rotting oranges, word ladders.
  • 7. Graphs (12 Paradigms, 22 Problems): Topological sort, cycle detection, Dijkstra's algorithm, bipartite graph validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens, Sudoku solver.
  • 9. Dynamic Programming (13 Paradigms, 28 Problems): Memoization vs tabulation, 0/1 knapsack, unbounded knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (9 Paradigms, 11 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (7 Paradigms, 17 Problems): Trie (prefix tree), Union-Find (Disjoint Set Union), LRU Cache, LFU Cache, range-sum queries with updates.
  • 12. Stack (5 Paradigms, 9 Problems): Next greater element, largest rectangle in histogram, remove k digits, decode string, min stack.
  • 13. Intervals & Greedy (2 Paradigms, 6 Problems): Merge intervals, interval scheduling, meeting rooms by sweep line, jump game.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Read the pattern's mental model, loop invariant and Big-O proof, compare the pattern's template with the problem's solution side by side, and use the step-by-step debugger. The solution can also be shown in C#, Java, TypeScript, C++, Go or Rust for almost every problem.
  2. Stage 2 (Active Recall): Rebuild the solution from memory in a code editor, with an invariant checklist and optional progressive hints, and check it against a judged test suite.
  3. Stage 3 (AI Interview Coach, coming soon): A mock-interview stage still in development; for now, students go straight from Stage 2 to Stage 4.
  4. Stage 4 (Solve on Your Own): Solve a new problem on your own against a 15-minute timer; your code is graded against a test suite. Python and TypeScript run in your browser and C# runs on our server (design problems grade in Python and TypeScript).

Equipped with a spaced-repetition review hub on a fixed 7-step schedule (1 day up to 90 days), a two-pane Studio workspace, and study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Coins: New accounts get 40 free coins once, the daily claim adds +20 coins once a day, and inviting a friend earns you +25 coins when they complete their first practice step. These are the only ways to earn coins.
  • AI Coach: Each AI coach question costs 1-5 coins, priced by the server according to its size.
  • BYOK (Bring Your Own Key): AI coaching is free with your own Google Gemini, OpenAI or OpenRouter API key (BYOK).
  • Extras: Coins also unlock extras for 24 hours: a Recall hint level (1 coin), a Solve solution reveal (2), a LeetCode problem card's solutions (1) and a pattern's extra canonical problems (1).
  • Payments & Refunds: Nothing on the site is sold for money: coins can only be earned, never bought. There are no subscriptions or charges, so there is nothing to refund.
  • Platform Operator: Hi👋WebEnterprise. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

Arena
238Items
Theory Context•Two Pointers & Sliding Window
MediumLC 164

Maximum Gap (LeetCode 164)

Target Frequency:AmazonGoogleMicrosoft

You get an integer array nums. Picture its values sorted from smallest to largest: each pair of values that end up side by side has a difference, and you must return the largest of those differences. If nums holds fewer than two values, there is no such pair, so return 0.

Sorting only defines the answer; it is not a way you may use to find it. Your algorithm has to run in time linear in the length of nums, and it may use extra space that is also linear in it.

Worked Examples

Example 1
Input:nums = [3,6,9,1]
Output:3
306192136 - 3 = 39 - 6 = 3
Explanation: Sorted, the values read 1, 3, 6, 9. The differences between neighbours are 2, 3 and 3, so the largest is 3; both 3 → 6 and 6 → 9 reach it.
Example 2
Input:nums = [10]
Output:0
100no neighbour
Explanation: A single value has no neighbour, so there is no difference to measure and the answer is 0.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 105

  • 0 <= nums[i] <= 109

Deep-Dive & Conceptual Insights

Core Insight

With width = max(1, (hi - lo) // (n - 1)), no bucket is wider than the smallest possible maximum gap, so the answer never lies inside a bucket: one min and one max per bucket, compared across buckets, is all you need.

Real-World Scenario & Production Applications

Finding the biggest hole in a set of readings: the longest silence between event timestamps, the widest unused block in a range of IDs or ports, the largest jump between sensor values. Bucketing by value finds it in two passes, without sorting millions of readings first.

Step-by-Step Execution Trace Table

Example 1, nums = [3,6,9,1]: n = 4, lo = 1, hi = 9, width = max(1, 8 // 3) = 2, count = 8 // 2 + 1 = 5. After the fill loop, the scan reads:

bRangebucket_min[b], bucket_max[b]Gap bucket_min[b] - prev_maxbestprev_max after
01..21, 11 - 1 = 001
13..43, 33 - 1 = 223
25..66, 66 - 3 = 336
37..8emptyskipped by continue: the gap runs across it36
49..109, 99 - 6 = 339
Worked on Paper

The Math, Step by Step

1. The number the code looks for

Imagine the nnn values of nums in sorted order: s0≤s1≤⋯≤sn−1s_0 \le s_1 \le \cdots \le s_{n-1}s0​≤s1​≤⋯≤sn−1​. The code never sorts them. The maximum gap is the largest difference between two neighbours, G=max⁡i(si+1−si)G = \max_i (s_{i+1} - s_i)G=maxi​(si+1​−si​). The code must find GGG in O(n)O(n)O(n) time, so it may not sort.

Let lo =s0= s_0=s0​ and hi =sn−1= s_{n-1}=sn−1​ be the smallest and the largest value.

2. The lower bound on GGG

The n−1n - 1n−1 neighbour differences add up to the whole range:

(s1−s0)+(s2−s1)+⋯+(sn−1−sn−2)=hi−lo(s_1 - s_0) + (s_2 - s_1) + \cdots + (s_{n-1} - s_{n-2}) = \text{hi} - \text{lo}(s1​−s0​)+(s2​−s1​)+⋯+(sn−1​−sn−2​)=hi−lo

The largest of n−1n - 1n−1 numbers is at least their average. So

G≥hi−lon−1G \ge \frac{\text{hi} - \text{lo}}{n - 1}G≥n−1hi−lo​

This is the pigeonhole principle at work: n−1n - 1n−1 gaps must cover the range, so at least one of them is at least the average size.

3. Buckets, and why the maximum gap is never inside one

The code splits the range into buckets of equal width:

width=max⁡ ⁣(1, ⌊hi−lon−1⌋)\text{width} = \max\!\left(1,\ \left\lfloor \frac{\text{hi} - \text{lo}}{n - 1} \right\rfloor\right)width=max(1, ⌊n−1hi−lo​⌋)

Bucket bbb holds the whole numbers from lo+b⋅width\text{lo} + b \cdot \text{width}lo+b⋅width to lo+(b+1)⋅width−1\text{lo} + (b + 1) \cdot \text{width} - 1lo+(b+1)⋅width−1. A value xxx falls into bucket ⌊(x−lo)/width⌋\lfloor (x - \text{lo}) / \text{width} \rfloor⌊(x−lo)/width⌋.

Two values in the same bucket differ by at most width−1\text{width} - 1width−1. By part 2, G≥widthG \ge \text{width}G≥width whenever the values are not all equal. So two values inside one bucket are always closer than GGG, and the maximum gap never lies inside a bucket. It always runs from the largest value of one non-empty bucket to the smallest value of the next non-empty bucket.

So each bucket needs only two numbers, its smallest and its largest value. The code never sorts inside a bucket.

4. The max(1, ...) and the + 1

When hi−lo<n−1\text{hi} - \text{lo} < n - 1hi−lo<n−1, the division gives 0, and a width of 0 would divide by zero. max(1, ...) makes the width at least 1. A bucket of width 1 holds one whole number, so values inside it differ by 0, and part 3 still holds.

The number of buckets is (hi - lo) // width + 1. The + 1 makes sure that hi has a bucket: hi falls into bucket ⌊(hi−lo)/width⌋\lfloor (\text{hi} - \text{lo}) / \text{width} \rfloor⌊(hi−lo)/width⌋, which is the last one.

prev_max starts at lo. The first non-empty bucket is bucket 0, which holds lo, so its first gap is lo−lo=0\text{lo} - \text{lo} = 0lo−lo=0. Empty buckets are skipped, so a gap can run across several of them.

With fewer than two values there is no neighbour at all, so the code returns 0 at once.

5. Example 1 by hand

nums = [3,6,9,1], so n=4n = 4n=4, lo = 1 and hi = 9. By part 2, G≥9−13=83≈2.67G \ge \frac{9 - 1}{3} = \frac{8}{3} \approx 2.67G≥39−1​=38​≈2.67. The code computes width =max⁡(1,⌊8/3⌋)=2= \max(1, \lfloor 8 / 3 \rfloor) = 2=max(1,⌊8/3⌋)=2 and count =⌊8/2⌋+1=5= \lfloor 8 / 2 \rfloor + 1 = 5=⌊8/2⌋+1=5 buckets.

BucketRangeValuesbucket_minbucket_max
01 to 2111
13 to 4333
25 to 6666
37 to 8noneemptyempty
49 to 10999
103162-394

The picture shows the five buckets of Example 1, one cell per bucket, with the bucket number under each cell. Each cell holds the value that fell into that bucket, and "-" marks the empty bucket 3. The gap from 6 in bucket 2 to 9 in bucket 4 runs across the empty bucket.

The second loop walks the non-empty buckets and measures each gap from the last bucket's largest value:

Bucketbucket_min[b] - prev_maxbestprev_max after
01−1=01 - 1 = 01−1=001
13−1=23 - 1 = 23−1=223
26−3=36 - 3 = 36−3=336
49−6=39 - 6 = 39−6=339

The code returns 3, the expected output. The sorted values are 1, 3, 6, 9, and both 6−36 - 36−3 and 9−69 - 69−6 reach 3.

6. Example 2 by hand

nums = [10], so n=1n = 1n=1. The check n < 2 is true, and the code returns 0, the expected output.

7. From the math to the code
MathIn maximumGap
no neighbours with fewer than 2 valuesif n < 2: followed by return 0
max⁡(1,⌊(hi−lo)/(n−1)⌋)\max(1, \lfloor (\text{hi} - \text{lo}) / (n - 1) \rfloor)max(1,⌊(hi−lo)/(n−1)⌋)width = max(1, (hi - lo) // (n - 1))
enough buckets to hold hicount = (hi - lo) // width + 1
the bucket of xxxb = (x - lo) // width
keep only the smallest and largest value per bucketbucket_min[b] = min(bucket_min[b], x) and bucket_max[b] = max(bucket_max[b], x)
skip empty bucketsif bucket_min[b] == float("inf"): followed by continue
gaps run between bucketsbest = max(best, bucket_min[b] - prev_max)

Because width is the rounded-down average gap, there are never more than 2n−12n - 12n−1 buckets. So both loops take O(n)O(n)O(n) time, and the bucket lists take O(n)O(n)O(n) space.

Code Shape
Archetype Code Shape
Python
1Don't sort: split `lo..hi` into buckets of equal `width` and drop each value into `b = (x - lo) // width`; the buckets come out in order with no comparisons.
2`n` values leave `n - 1` gaps that add up to `hi - lo`, so the largest gap is at least `width`: it never sits inside one bucket. Keep only `bucket_min[b]` and `bucket_max[b]`.
3Return `0` if `n < 2`; set `width` and `count = (hi - lo) // width + 1`; fill both lists in one pass; then scan `b` in order, skip empty buckets, and track `best = max(best, bucket_min[b] - prev_max)` with `prev_max = bucket_max[b]`.
4The trap: `width = max(1, (hi - lo) // (n - 1))`. With repeated values `hi - lo < n - 1`, the plain quotient is `0`, and `(x - lo) // width` divides by zero.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Sorting would answer this at once: sort, then read the differences between neighbours. But the problem asks for linear time, and a comparison sort costs O(Nlog⁡N)O(N \log N)O(NlogN). Bucket Sort avoids comparisons. Split the range from lo = min(nums) to hi = max(nums) into buckets of equal width, and drop each value into its bucket with b = (x - lo) // width: the buckets come out in order without any two values being compared. The values inside one bucket stay unsorted, and that is fine, because with the right width the answer never lives inside a bucket. So each bucket keeps only two numbers, bucket_min[b] and bucket_max[b].

🗄️ The Analogy: Letters in Pigeonholes

A mail room has n letters with house numbers from lo to hi, and a row of pigeonholes, each covering the same stretch of house numbers. You want the biggest jump between two house numbers that come one after the other. The n - 1 jumps together add up to hi - lo, so at least one of them is as long as their average. Make each pigeonhole no longer than that average, and two letters in the same pigeonhole are always closer than the biggest jump. So you never look inside a pigeonhole: you only need its lowest and highest letter, and the biggest jump is always from the highest letter of one pigeonhole to the lowest letter of the next pigeonhole that holds any.

🔑 Why It Works

This is the pigeonhole principle in three lines. n values leave n - 1 gaps that add up to hi - lo, so the largest gap is at least (hi - lo) / (n - 1); it is a whole number, so when hi > lo it is at least width. Two values that share a bucket differ by at most width - 1, so they can never be the answer: the maximum gap always runs from one bucket's bucket_max to the bucket_min of the next non-empty bucket, called prev_max and bucket_min[b] in the scan. The max(1, ...) matters: when there are more values than steps between lo and hi (hi - lo < n - 1, for example with repeated values), the plain quotient is 0 and (x - lo) // width would divide by zero.

  • Zero-width buckets: write width = max(1, (hi - lo) // (n - 1)). On [1,1,1,4,2], hi - lo = 3 is less than n - 1 = 4, so the plain quotient is 0 and (x - lo) // width raises a division by zero.

  • Buckets wider than the smallest possible gap: round the width down. With (hi - lo) // (n - 1) + 1, [6,18] gets width = 13, both values land in bucket 0, and the answer comes out 0 instead of 12.

  • Comparing neighbouring bucket indices: skip empty buckets and carry prev_max from the last non-empty one. The largest gap usually runs across empty buckets, so bucket_min[b] - bucket_max[b - 1] misses it or meets inf.

  • Forgetting the one-value case: if n < 2: return 0 must come first; with one value, n - 1 is 0 and the width itself divides by zero.

  • Sorting anyway: sorted(nums) gives the right answer in O(N log N), but the problem asks for linear time; the pigeonhole bound on width is what makes it O(N).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"The maximum difference between successive elements in sorted form" plus "linear time and linear extra space": the answer is defined by sorting, but a comparison sort is too slow. Values bounded by min(nums) and max(nums) and a question about the gaps between them is the signal for Bucket Sort with pigeonhole-sized buckets.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Decision rule: drop each x into b = (x - lo) // width, keeping only bucket_min[b] and bucket_max[b]; then, for each non-empty bucket in order, best = max(best, bucket_min[b] - prev_max) and prev_max = bucket_max[b].

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The floor of 1 in width = max(1, (hi - lo) // (n - 1)): without it, repeated values make the width 0 and (x - lo) // width divides by zero.

  • count = (hi - lo) // width + 1, not (hi - lo) // width: hi itself lands in bucket (hi - lo) // width, so without the + 1 it indexes past the end ([1, 10] has width = 9 and needs 2 buckets).

  • Start prev_max = lo, not 0: bucket 0 holds lo, so its first gap is 0 and harmless, while 0 measures a gap from 0 up to lo that isn't in the input ([100, 101] would return 100 instead of 1).

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd solve this with Bucket Sort. Sorting defines the answer, but a comparison sort is too slow, so I bucket by value. The n minus 1 gaps add up to hi minus lo, so the largest gap is at least their average, and I make every bucket narrower than that: width is hi minus lo, integer-divided by n minus 1, and at least 1. Then the maximum gap never sits inside a bucket, so each bucket keeps only its min and max. Then I walk the buckets in order, skip empty ones, and track the largest bucket min minus the previous max. The trap is the floor of 1: with many repeated values the quotient is zero and I'd divide by zero. Two linear passes, so O(N) time and O(N) space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: len, min and max each read nums once, O(N). The fill loop for x in nums does one division and two comparisons per value, O(N). The number of buckets is count = (hi - lo) // width + 1. If the quotient (hi - lo) // (n - 1) is at least 1, it is at least half of (hi - lo) / (n - 1), so count <= 2(n - 1) + 1; if it is 0, then width = 1 and hi - lo < n - 1, so count <= n - 1. Either way count is O(N), so building the two bucket lists and the scan for b in range(count) are O(N) too. Total: O(N).

SPACE COMPLEXITY

O(N)

bucket_min and bucket_max hold count entries each, so the extra space is O(N); everything else is a handful of numbers, and the answer is one integer.

Formal Recurrence Relation

T(N) = O(N) + O(count) + N · O(1) + count · O(1) = O(N), since count <= 2(N - 1) + 1

Derivation Progression

Bounds

O(N)

len(nums), min(nums) and max(nums) each read nums once.

Bucket lists

O(count) = O(N)

[float("inf")] * count and [float("-inf")] * count; count <= 2(N - 1) + 1.

Fill loop

N · O(1)

for x in nums: one division (x - lo) // width, one min and one max per value.

Scan

count · O(1)

for b in range(count): one emptiness check, then at most one subtraction, one max and one assignment per bucket.

Total

O(N)

Two linear passes over the values and one over at most 2(N - 1) + 1 buckets.

Variable Definitions

NNN

Number of values, len(nums) (called n in the code)

widthwidthwidth

Bucket width, max(1, (hi - lo) // (n - 1))

countcountcount

Number of buckets, (hi - lo) // width + 1, at most 2(N - 1) + 1

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion

🔵 Auxiliary Heap

O(N): bucket_min and bucket_max, count entries each

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1): one value, so if n < 2: return 0 answers at once

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): every value is bucketed and every bucket is scanned

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the maximum difference between successive elements in sorted form", "linear time and linear extra space". The answer is defined by sorting, but a comparison sort is too slow: Bucket Sort with pigeonhole-sized buckets, keeping one min and one max per bucket.

CONSTRAINTS & BOUNDS

N≤105N \le 10^5N≤105 values up to 10910^9109. Sorting costs O(Nlog⁡N)O(N \log N)O(NlogN) comparisons, and a counting array indexed by value would need 10910^9109 slots. Pigeonhole buckets need at most 2(N−1)+12(N - 1) + 12(N−1)+1 slots and two linear passes: O(N)O(N)O(N) time and space.

FAANG PRODUCTION TRAPS & EDGE CASES

On a stream, lo and hi must be known before the first value is bucketed, so the data needs one pass for the bounds and one for the buckets. x - lo stays below 10910^9109, so it fits a 32-bit integer.

Core Algorithmic State Invariants

  1. The Bucket Count Grows With n, Not With the Values

width is (hi - lo) // (n - 1), at least 1, so count = (hi - lo) // width + 1 is at most 2(n - 1) + 1. Memory depends on how many values there are, not on how far apart they are, which is what makes values up to 10^9 affordable.

  1. Placing Needs No Comparison

b = (x - lo) // width puts each value in its bucket by arithmetic alone, with no comparison against other values. The only comparisons are the two per value that update bucket_min[b] and bucket_max[b], O(1) each.

Theory Context•Two Pointers & Sliding Window
MediumLC 164

Maximum Gap (LeetCode 164)

Target Frequency:AmazonGoogleMicrosoft

You get an integer array nums. Picture its values sorted from smallest to largest: each pair of values that end up side by side has a difference, and you must return the largest of those differences. If nums holds fewer than two values, there is no such pair, so return 0.

Sorting only defines the answer; it is not a way you may use to find it. Your algorithm has to run in time linear in the length of nums, and it may use extra space that is also linear in it.

Worked Examples

Example 1
Input:nums = [3,6,9,1]
Output:3
306192136 - 3 = 39 - 6 = 3
Explanation: Sorted, the values read 1, 3, 6, 9. The differences between neighbours are 2, 3 and 3, so the largest is 3; both 3 → 6 and 6 → 9 reach it.
Example 2
Input:nums = [10]
Output:0
100no neighbour
Explanation: A single value has no neighbour, so there is no difference to measure and the answer is 0.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 105

  • 0 <= nums[i] <= 109

Deep-Dive & Conceptual Insights

Core Insight

With width = max(1, (hi - lo) // (n - 1)), no bucket is wider than the smallest possible maximum gap, so the answer never lies inside a bucket: one min and one max per bucket, compared across buckets, is all you need.

Real-World Scenario & Production Applications

Finding the biggest hole in a set of readings: the longest silence between event timestamps, the widest unused block in a range of IDs or ports, the largest jump between sensor values. Bucketing by value finds it in two passes, without sorting millions of readings first.

Step-by-Step Execution Trace Table

Example 1, nums = [3,6,9,1]: n = 4, lo = 1, hi = 9, width = max(1, 8 // 3) = 2, count = 8 // 2 + 1 = 5. After the fill loop, the scan reads:

bRangebucket_min[b], bucket_max[b]Gap bucket_min[b] - prev_maxbestprev_max after
01..21, 11 - 1 = 001
13..43, 33 - 1 = 223
25..66, 66 - 3 = 336
37..8emptyskipped by continue: the gap runs across it36
49..109, 99 - 6 = 339
Worked on Paper

The Math, Step by Step

1. The number the code looks for

Imagine the nnn values of nums in sorted order: s0≤s1≤⋯≤sn−1s_0 \le s_1 \le \cdots \le s_{n-1}s0​≤s1​≤⋯≤sn−1​. The code never sorts them. The maximum gap is the largest difference between two neighbours, G=max⁡i(si+1−si)G = \max_i (s_{i+1} - s_i)G=maxi​(si+1​−si​). The code must find GGG in O(n)O(n)O(n) time, so it may not sort.

Let lo =s0= s_0=s0​ and hi =sn−1= s_{n-1}=sn−1​ be the smallest and the largest value.

2. The lower bound on GGG

The n−1n - 1n−1 neighbour differences add up to the whole range:

(s1−s0)+(s2−s1)+⋯+(sn−1−sn−2)=hi−lo(s_1 - s_0) + (s_2 - s_1) + \cdots + (s_{n-1} - s_{n-2}) = \text{hi} - \text{lo}(s1​−s0​)+(s2​−s1​)+⋯+(sn−1​−sn−2​)=hi−lo

The largest of n−1n - 1n−1 numbers is at least their average. So

G≥hi−lon−1G \ge \frac{\text{hi} - \text{lo}}{n - 1}G≥n−1hi−lo​

This is the pigeonhole principle at work: n−1n - 1n−1 gaps must cover the range, so at least one of them is at least the average size.

3. Buckets, and why the maximum gap is never inside one

The code splits the range into buckets of equal width:

width=max⁡ ⁣(1, ⌊hi−lon−1⌋)\text{width} = \max\!\left(1,\ \left\lfloor \frac{\text{hi} - \text{lo}}{n - 1} \right\rfloor\right)width=max(1, ⌊n−1hi−lo​⌋)

Bucket bbb holds the whole numbers from lo+b⋅width\text{lo} + b \cdot \text{width}lo+b⋅width to lo+(b+1)⋅width−1\text{lo} + (b + 1) \cdot \text{width} - 1lo+(b+1)⋅width−1. A value xxx falls into bucket ⌊(x−lo)/width⌋\lfloor (x - \text{lo}) / \text{width} \rfloor⌊(x−lo)/width⌋.

Two values in the same bucket differ by at most width−1\text{width} - 1width−1. By part 2, G≥widthG \ge \text{width}G≥width whenever the values are not all equal. So two values inside one bucket are always closer than GGG, and the maximum gap never lies inside a bucket. It always runs from the largest value of one non-empty bucket to the smallest value of the next non-empty bucket.

So each bucket needs only two numbers, its smallest and its largest value. The code never sorts inside a bucket.

4. The max(1, ...) and the + 1

When hi−lo<n−1\text{hi} - \text{lo} < n - 1hi−lo<n−1, the division gives 0, and a width of 0 would divide by zero. max(1, ...) makes the width at least 1. A bucket of width 1 holds one whole number, so values inside it differ by 0, and part 3 still holds.

The number of buckets is (hi - lo) // width + 1. The + 1 makes sure that hi has a bucket: hi falls into bucket ⌊(hi−lo)/width⌋\lfloor (\text{hi} - \text{lo}) / \text{width} \rfloor⌊(hi−lo)/width⌋, which is the last one.

prev_max starts at lo. The first non-empty bucket is bucket 0, which holds lo, so its first gap is lo−lo=0\text{lo} - \text{lo} = 0lo−lo=0. Empty buckets are skipped, so a gap can run across several of them.

With fewer than two values there is no neighbour at all, so the code returns 0 at once.

5. Example 1 by hand

nums = [3,6,9,1], so n=4n = 4n=4, lo = 1 and hi = 9. By part 2, G≥9−13=83≈2.67G \ge \frac{9 - 1}{3} = \frac{8}{3} \approx 2.67G≥39−1​=38​≈2.67. The code computes width =max⁡(1,⌊8/3⌋)=2= \max(1, \lfloor 8 / 3 \rfloor) = 2=max(1,⌊8/3⌋)=2 and count =⌊8/2⌋+1=5= \lfloor 8 / 2 \rfloor + 1 = 5=⌊8/2⌋+1=5 buckets.

BucketRangeValuesbucket_minbucket_max
01 to 2111
13 to 4333
25 to 6666
37 to 8noneemptyempty
49 to 10999
103162-394

The picture shows the five buckets of Example 1, one cell per bucket, with the bucket number under each cell. Each cell holds the value that fell into that bucket, and "-" marks the empty bucket 3. The gap from 6 in bucket 2 to 9 in bucket 4 runs across the empty bucket.

The second loop walks the non-empty buckets and measures each gap from the last bucket's largest value:

Bucketbucket_min[b] - prev_maxbestprev_max after
01−1=01 - 1 = 01−1=001
13−1=23 - 1 = 23−1=223
26−3=36 - 3 = 36−3=336
49−6=39 - 6 = 39−6=339

The code returns 3, the expected output. The sorted values are 1, 3, 6, 9, and both 6−36 - 36−3 and 9−69 - 69−6 reach 3.

6. Example 2 by hand

nums = [10], so n=1n = 1n=1. The check n < 2 is true, and the code returns 0, the expected output.

7. From the math to the code
MathIn maximumGap
no neighbours with fewer than 2 valuesif n < 2: followed by return 0
max⁡(1,⌊(hi−lo)/(n−1)⌋)\max(1, \lfloor (\text{hi} - \text{lo}) / (n - 1) \rfloor)max(1,⌊(hi−lo)/(n−1)⌋)width = max(1, (hi - lo) // (n - 1))
enough buckets to hold hicount = (hi - lo) // width + 1
the bucket of xxxb = (x - lo) // width
keep only the smallest and largest value per bucketbucket_min[b] = min(bucket_min[b], x) and bucket_max[b] = max(bucket_max[b], x)
skip empty bucketsif bucket_min[b] == float("inf"): followed by continue
gaps run between bucketsbest = max(best, bucket_min[b] - prev_max)

Because width is the rounded-down average gap, there are never more than 2n−12n - 12n−1 buckets. So both loops take O(n)O(n)O(n) time, and the bucket lists take O(n)O(n)O(n) space.

Code Shape
Archetype Code Shape
Python
1Don't sort: split `lo..hi` into buckets of equal `width` and drop each value into `b = (x - lo) // width`; the buckets come out in order with no comparisons.
2`n` values leave `n - 1` gaps that add up to `hi - lo`, so the largest gap is at least `width`: it never sits inside one bucket. Keep only `bucket_min[b]` and `bucket_max[b]`.
3Return `0` if `n < 2`; set `width` and `count = (hi - lo) // width + 1`; fill both lists in one pass; then scan `b` in order, skip empty buckets, and track `best = max(best, bucket_min[b] - prev_max)` with `prev_max = bucket_max[b]`.
4The trap: `width = max(1, (hi - lo) // (n - 1))`. With repeated values `hi - lo < n - 1`, the plain quotient is `0`, and `(x - lo) // width` divides by zero.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Sorting would answer this at once: sort, then read the differences between neighbours. But the problem asks for linear time, and a comparison sort costs O(Nlog⁡N)O(N \log N)O(NlogN). Bucket Sort avoids comparisons. Split the range from lo = min(nums) to hi = max(nums) into buckets of equal width, and drop each value into its bucket with b = (x - lo) // width: the buckets come out in order without any two values being compared. The values inside one bucket stay unsorted, and that is fine, because with the right width the answer never lives inside a bucket. So each bucket keeps only two numbers, bucket_min[b] and bucket_max[b].

🗄️ The Analogy: Letters in Pigeonholes

A mail room has n letters with house numbers from lo to hi, and a row of pigeonholes, each covering the same stretch of house numbers. You want the biggest jump between two house numbers that come one after the other. The n - 1 jumps together add up to hi - lo, so at least one of them is as long as their average. Make each pigeonhole no longer than that average, and two letters in the same pigeonhole are always closer than the biggest jump. So you never look inside a pigeonhole: you only need its lowest and highest letter, and the biggest jump is always from the highest letter of one pigeonhole to the lowest letter of the next pigeonhole that holds any.

🔑 Why It Works

This is the pigeonhole principle in three lines. n values leave n - 1 gaps that add up to hi - lo, so the largest gap is at least (hi - lo) / (n - 1); it is a whole number, so when hi > lo it is at least width. Two values that share a bucket differ by at most width - 1, so they can never be the answer: the maximum gap always runs from one bucket's bucket_max to the bucket_min of the next non-empty bucket, called prev_max and bucket_min[b] in the scan. The max(1, ...) matters: when there are more values than steps between lo and hi (hi - lo < n - 1, for example with repeated values), the plain quotient is 0 and (x - lo) // width would divide by zero.

  • Zero-width buckets: write width = max(1, (hi - lo) // (n - 1)). On [1,1,1,4,2], hi - lo = 3 is less than n - 1 = 4, so the plain quotient is 0 and (x - lo) // width raises a division by zero.

  • Buckets wider than the smallest possible gap: round the width down. With (hi - lo) // (n - 1) + 1, [6,18] gets width = 13, both values land in bucket 0, and the answer comes out 0 instead of 12.

  • Comparing neighbouring bucket indices: skip empty buckets and carry prev_max from the last non-empty one. The largest gap usually runs across empty buckets, so bucket_min[b] - bucket_max[b - 1] misses it or meets inf.

  • Forgetting the one-value case: if n < 2: return 0 must come first; with one value, n - 1 is 0 and the width itself divides by zero.

  • Sorting anyway: sorted(nums) gives the right answer in O(N log N), but the problem asks for linear time; the pigeonhole bound on width is what makes it O(N).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"The maximum difference between successive elements in sorted form" plus "linear time and linear extra space": the answer is defined by sorting, but a comparison sort is too slow. Values bounded by min(nums) and max(nums) and a question about the gaps between them is the signal for Bucket Sort with pigeonhole-sized buckets.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Decision rule: drop each x into b = (x - lo) // width, keeping only bucket_min[b] and bucket_max[b]; then, for each non-empty bucket in order, best = max(best, bucket_min[b] - prev_max) and prev_max = bucket_max[b].

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • The floor of 1 in width = max(1, (hi - lo) // (n - 1)): without it, repeated values make the width 0 and (x - lo) // width divides by zero.

  • count = (hi - lo) // width + 1, not (hi - lo) // width: hi itself lands in bucket (hi - lo) // width, so without the + 1 it indexes past the end ([1, 10] has width = 9 and needs 2 buckets).

  • Start prev_max = lo, not 0: bucket 0 holds lo, so its first gap is 0 and harmless, while 0 measures a gap from 0 up to lo that isn't in the input ([100, 101] would return 100 instead of 1).

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd solve this with Bucket Sort. Sorting defines the answer, but a comparison sort is too slow, so I bucket by value. The n minus 1 gaps add up to hi minus lo, so the largest gap is at least their average, and I make every bucket narrower than that: width is hi minus lo, integer-divided by n minus 1, and at least 1. Then the maximum gap never sits inside a bucket, so each bucket keeps only its min and max. Then I walk the buckets in order, skip empty ones, and track the largest bucket min minus the previous max. The trap is the floor of 1: with many repeated values the quotient is zero and I'd divide by zero. Two linear passes, so O(N) time and O(N) space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: len, min and max each read nums once, O(N). The fill loop for x in nums does one division and two comparisons per value, O(N). The number of buckets is count = (hi - lo) // width + 1. If the quotient (hi - lo) // (n - 1) is at least 1, it is at least half of (hi - lo) / (n - 1), so count <= 2(n - 1) + 1; if it is 0, then width = 1 and hi - lo < n - 1, so count <= n - 1. Either way count is O(N), so building the two bucket lists and the scan for b in range(count) are O(N) too. Total: O(N).

SPACE COMPLEXITY

O(N)

bucket_min and bucket_max hold count entries each, so the extra space is O(N); everything else is a handful of numbers, and the answer is one integer.

Formal Recurrence Relation

T(N) = O(N) + O(count) + N · O(1) + count · O(1) = O(N), since count <= 2(N - 1) + 1

Derivation Progression

Bounds

O(N)

len(nums), min(nums) and max(nums) each read nums once.

Bucket lists

O(count) = O(N)

[float("inf")] * count and [float("-inf")] * count; count <= 2(N - 1) + 1.

Fill loop

N · O(1)

for x in nums: one division (x - lo) // width, one min and one max per value.

Scan

count · O(1)

for b in range(count): one emptiness check, then at most one subtraction, one max and one assignment per bucket.

Total

O(N)

Two linear passes over the values and one over at most 2(N - 1) + 1 buckets.

Variable Definitions

NNN

Number of values, len(nums) (called n in the code)

widthwidthwidth

Bucket width, max(1, (hi - lo) // (n - 1))

countcountcount

Number of buckets, (hi - lo) // width + 1, at most 2(N - 1) + 1

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion

🔵 Auxiliary Heap

O(N): bucket_min and bucket_max, count entries each

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(1)O(1)O(1): one value, so if n < 2: return 0 answers at once

Average Case

O(N)O(N)O(N)

Worst Case

O(N)O(N)O(N): every value is bucketed and every bucket is scanned

Pointer Invariant Transition Progression

Pointer Invariant Transition Progression
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the maximum difference between successive elements in sorted form", "linear time and linear extra space". The answer is defined by sorting, but a comparison sort is too slow: Bucket Sort with pigeonhole-sized buckets, keeping one min and one max per bucket.

CONSTRAINTS & BOUNDS

N≤105N \le 10^5N≤105 values up to 10910^9109. Sorting costs O(Nlog⁡N)O(N \log N)O(NlogN) comparisons, and a counting array indexed by value would need 10910^9109 slots. Pigeonhole buckets need at most 2(N−1)+12(N - 1) + 12(N−1)+1 slots and two linear passes: O(N)O(N)O(N) time and space.

FAANG PRODUCTION TRAPS & EDGE CASES

On a stream, lo and hi must be known before the first value is bucketed, so the data needs one pass for the bounds and one for the buckets. x - lo stays below 10910^9109, so it fits a 32-bit integer.

Core Algorithmic State Invariants

  1. The Bucket Count Grows With n, Not With the Values

width is (hi - lo) // (n - 1), at least 1, so count = (hi - lo) // width + 1 is at most 2(n - 1) + 1. Memory depends on how many values there are, not on how far apart they are, which is what makes values up to 10^9 affordable.

  1. Placing Needs No Comparison

b = (x - lo) // width puts each value in its bucket by arithmetic alone, with no comparison against other values. The only comparisons are the two per value that update bucket_min[b] and bucket_max[b], O(1) each.

© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•