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 1438

Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (LeetCode 1438)

Target Frequency:GoogleAmazon

Take the integer array nums and an integer limit. Look for the longest non-empty contiguous run of nums (a subarray) in which every pair of elements inside the run differs by at most limit.

Equivalently, the run's largest value minus its smallest value must be at most limit. Return the length of the longest such run.

Worked Examples

Example 1
Input:nums = [8,2,4,7], limit = 4
Output:2
80214273
Explanation: [2,4] has a gap of 2 and [4,7] has a gap of 3, both within the limit. Every run of three or more elements contains a pair more than 4 apart (for example 8 and 2 differ by 6), so the longest valid run has length 2.
Example 2
Input:nums = [10,1,2,4,7,2], limit = 5
Output:4
1001122437425
Explanation: The subarray [2,4,7,2] has maximum 7 and minimum 2, a gap of exactly 5, which fits the limit; no longer run does.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 105

  • 1 <= nums[i] <= 109

  • 0 <= limit <= 109

Deep-Dive & Conceptual Insights

Core Insight

Two deques read the same window side by side: max_dq strictly decreasing gives the window's maximum at its front, min_dq strictly increasing gives the minimum at its front, so nums[max_dq[0]] - nums[min_dq[0]] is the window's exact spread with no rescan. Shrinking the window checks both fronts on every step, because each deque only evicts the indices it has itself checked.

Real-World Scenario & Production Applications

Alerting systems that only fire while a sensor or price feed's recent range (its maximum minus its minimum) stays inside a tolerance band, such as a thermostat's calm range or a trading band that should not fire on ordinary noise.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Keep Both Deques Monotonic

max_dq pops every value <= nums[right] from the back, so its values strictly decrease. min_dq pops every value >= nums[right] from the back, so its values strictly increase. Each rule is the single-deque Monotonic Deque rule, run on its own side.

Mathematical Recurrence / Code Invariant
while max_dq and nums[max_dq[-1]] <= nums[right]:
    max_dq.pop()
max_dq.append(right)
while min_dq and nums[min_dq[-1]] >= nums[right]:
    min_dq.pop()
min_dq.append(right)

Step-by-Step Execution Trace Table

rightnums[right]max_dq after pushmin_dq after pushShrink while spread > limitbest
08[0] (values 8)[0] (values 8)spread 0, no shrink1
12[0, 1] (values 8, 2)[1] (pops 0: 8 >= 2)spread 6 > 4: left -> 1, max_dq drops 0 -> [1]1
24[2] (pops 1: 2 <= 4)[1, 2] (values 2, 4)spread 2, no shrink2
37[3] (pops 2: 4 <= 7)[1, 2, 3] (values 2, 4, 7)spread 5 > 4: left -> 2, min_dq drops 1 -> [2, 3]2
Code Shape
Archetype Code Shape
Python
1max_dq, min_dq = deque(), deque()
2left = 0
3best = 0
4for right in range(len(nums)):
5 while max_dq and nums[max_dq[-1]] <= nums[right]:
6 max_dq.pop()
7 max_dq.append(right)
8 while min_dq and nums[min_dq[-1]] >= nums[right]:
9 min_dq.pop()
10 min_dq.append(right)
11 while nums[max_dq[0]] - nums[min_dq[0]] > limit:
12 left += 1
13 if max_dq[0] < left: max_dq.popleft()
14 if min_dq[0] < left: min_dq.popleft()
15 best = max(best, right - left + 1)
16return best

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The Monotonic Deque card gives a window's maximum (or its minimum) in O(1) per step. This problem needs both the maximum and the minimum of the same window at once, because the window is only valid while they stay within limit of each other. Running one deque of each kind side by side, over the same two pointers, answers both questions together without rescanning the window.

⚖️ The Analogy: Two Leaderboards, Tallest and Shortest, Watching the Same Room

Keep two leaderboards for the same room of people: one for "tallest so far," one for "shortest so far," exactly like the single-deque leaderboard, run twice. Every time someone new walks in, both leaderboards update by their own rule. When someone leaves (the window shrinks), each leaderboard must check its own front on its own: the tallest-so-far leaderboard might still have a current leader standing in the room, while the shortest-so-far leaderboard's current leader already walked out. Checking only one leaderboard when someone leaves means the other can keep naming someone who isn't there anymore.

🔑 The Key Idea in the Code

nums[max_dq[0]] - nums[min_dq[0]] is the window's exact spread, read with no scan. While that spread is greater than limit, left moves forward one step, and both if max_dq[0] < left and if min_dq[0] < left run every time, because each deque only knows about the indices it has personally evicted.

  • Shrinking checks only max_dq's stale front, never min_dq's

  • Writing >= instead of <= when popping max_dq's back

  • Evicting a stale front on <= instead of <

  • Storing values instead of indices in max_dq and min_dq

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks about every pair of elements inside a subarray, which is the same as its maximum minus its minimum. The window's validity depends on both running extremes at once, which a single Monotonic Deque cannot answer, since it only tracks one side.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Decision at each right: keep max_dq strictly decreasing and min_dq strictly increasing, then while nums[max_dq[0]] - nums[min_dq[0]] > limit, move left forward and check max_dq[0] < left and min_dq[0] < left separately before recording right - left + 1.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • if max_dq[0] < left: ... if min_dq[0] < left: ...: both checks run on every shrink step, never only one (gotcha 1 has the failing input).

  • while max_dq and nums[max_dq[-1]] <= nums[right]:: keep the <= direction for the max deque, >= for the min deque, never swapped (gotcha 2).

  • if max_dq[0] < left:: strictly <, not <=, since index left itself is still inside the window (gotcha 3).

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'll use a Dual Monotonic Deque. I run two Monotonic Deques over the same window: max_dq strictly decreasing, so its front is the window's maximum, and min_dq strictly increasing, so its front is the window's minimum. For each right, I push right onto both deques after popping whatever it dominates. Then, while the window's spread, nums of max_dq's front minus nums of min_dq's front, is over the limit, I move left forward one step and check both fronts on their own: if max_dq's front fell behind left I pop it, and separately if min_dq's front fell behind left I pop it. The trap is checking only one of those two: each deque only evicts what it has itself checked, so one front can go stale before the other. After the window is valid again, I record right minus left plus one as a candidate length. Every index is pushed into each deque once and popped at most once, so it's O(N) time and O(N) space for the two deques."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: for right in range(len(nums)) runs N times. Inside it, max_dq.append(right) and min_dq.append(right) are one push each, and the two while loops that pop from their backs (nums[max_dq[-1]] <= nums[right], nums[min_dq[-1]] >= nums[right]) can run many times in one step, but each pop removes an index that was appended earlier, and every index is appended exactly once into each deque, so across the whole run each deque's back pops total at most N. The shrink loop while nums[max_dq[0]] - nums[min_dq[0]] > limit moves left forward; left only increases and never exceeds right, and each of its if max_dq[0] < left / if min_dq[0] < left pops an index from the front of its own deque at most once, so these front pops also total at most N each across the whole run. Adding up: N pushes and at most N back-pops per deque, plus at most N front-pops per deque from the shrink loop: O(N) total, not O(N) per deque doubled into something larger, since every bound here is already linear in N.

SPACE COMPLEXITY

O(N)

Look at the allocations: max_dq and min_dq each hold only indices still inside the window nums[left..right], so together they never exceed 2N entries, still O(N). There is no recursion, and the answer best is one integer.

Formal Recurrence Relation

T(N)=2N⏟appends, both deques+≤2N⏟back pops+≤2N⏟front pops from shrinking=O(N)T(N) = \underbrace{2N}_{\text{appends, both deques}} + \underbrace{\le 2N}_{\text{back pops}} + \underbrace{\le 2N}_{\text{front pops from shrinking}} = O(N)T(N)=appends, both deques2N​​+back pops≤2N​​+front pops from shrinking≤2N​​=O(N)

Derivation Progression

Outer loop

for right in range(len(nums)): N passes, each pushing once into max_dq and once into min_dq

Everything outside the two inner while loops and the shrink loop costs a constant amount per index.

Back pops, counted over the whole run

total max_dq.pop() <= total max_dq.append(right) = N; same bound for min_dq

An index can only be popped from a deque's back after it was appended to that same deque, and each index is appended once per deque, so each deque's inner while pops at most N times across all passes together.

Front pops from shrinking, counted over the whole run

total max_dq.popleft() + total min_dq.popleft() <= N, since left only increases and left <= right <= N

left starts at 0 and only increases, and it never exceeds right, so it increases at most N times across the whole run; each increase pops at most one index from each deque's front.

Total

2N + 2N + 2N = O(N)

Appends, back pops and front pops on both deques together add up to a constant times N.

Variable Definitions

NNN

len(nums), the number of values

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(N): max_dq and min_dq together hold at most 2N indices inside the current window

🟢 Output Space

O(1): best, a single integer

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): every value equal, nothing is ever popped from either deque's back, and the window never has to shrink

Average Case

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

Worst Case

O(N)O(N)O(N): an alternating high-low pattern, where every new value pops something from one deque's back and the window must shrink often

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: "every pair of elements in this subarray", "absolute difference... less than or equal to limit". The window's validity depends on both its maximum and its minimum at once: two Monotonic Deques, one of each kind, not a single deque and not a heap of pairs.

CONSTRAINTS & BOUNDS

1≤N≤1051 \le N \le 10^51≤N≤105, values in [1,109][1, 10^9][1,109]. Checking every pair inside a candidate subarray is O(N3)O(N^3)O(N3) in the worst case; a shrinking window with two deques is O(N)O(N)O(N), since each index is pushed once into each deque and popped from it at most once.

FAANG PRODUCTION TRAPS & EDGE CASES

THE SHARED-SHRINK TRAP: when the window shrinks, each deque's front is checked on its own if, never only one of them, because a deque only evicts the indices it has itself checked; skipping one check leaves a stale front that no longer belongs to the window.

Core Algorithmic State Invariants

  1. Two Deques, Two Independent Fronts

max_dq and min_dq each run the same Monotonic Deque rule on their own side, so each deque only ever evicts the indices it has itself checked. One deque's front can fall behind left before the other's does, which is why the shrink loop tests max_dq[0] and min_dq[0] separately, on every step, instead of assuming one check covers both.

  1. The Window's Spread, Read Without a Scan

nums[max_dq[0]] - nums[min_dq[0]] is the window's exact maximum minus its exact minimum, because both fronts are kept current by the same rule that keeps a single Monotonic Deque's front current. No element of the window is ever re-read to compute this.

Theory Context•Two Pointers & Sliding Window
MediumLC 1438

Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (LeetCode 1438)

Target Frequency:GoogleAmazon

Take the integer array nums and an integer limit. Look for the longest non-empty contiguous run of nums (a subarray) in which every pair of elements inside the run differs by at most limit.

Equivalently, the run's largest value minus its smallest value must be at most limit. Return the length of the longest such run.

Worked Examples

Example 1
Input:nums = [8,2,4,7], limit = 4
Output:2
80214273
Explanation: [2,4] has a gap of 2 and [4,7] has a gap of 3, both within the limit. Every run of three or more elements contains a pair more than 4 apart (for example 8 and 2 differ by 6), so the longest valid run has length 2.
Example 2
Input:nums = [10,1,2,4,7,2], limit = 5
Output:4
1001122437425
Explanation: The subarray [2,4,7,2] has maximum 7 and minimum 2, a gap of exactly 5, which fits the limit; no longer run does.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 105

  • 1 <= nums[i] <= 109

  • 0 <= limit <= 109

Deep-Dive & Conceptual Insights

Core Insight

Two deques read the same window side by side: max_dq strictly decreasing gives the window's maximum at its front, min_dq strictly increasing gives the minimum at its front, so nums[max_dq[0]] - nums[min_dq[0]] is the window's exact spread with no rescan. Shrinking the window checks both fronts on every step, because each deque only evicts the indices it has itself checked.

Real-World Scenario & Production Applications

Alerting systems that only fire while a sensor or price feed's recent range (its maximum minus its minimum) stays inside a tolerance band, such as a thermostat's calm range or a trading band that should not fire on ordinary noise.

Subproblems & Recurrence Decomposition3 Phases

🧩Subproblem 1: Keep Both Deques Monotonic

max_dq pops every value <= nums[right] from the back, so its values strictly decrease. min_dq pops every value >= nums[right] from the back, so its values strictly increase. Each rule is the single-deque Monotonic Deque rule, run on its own side.

Mathematical Recurrence / Code Invariant
while max_dq and nums[max_dq[-1]] <= nums[right]:
    max_dq.pop()
max_dq.append(right)
while min_dq and nums[min_dq[-1]] >= nums[right]:
    min_dq.pop()
min_dq.append(right)

Step-by-Step Execution Trace Table

rightnums[right]max_dq after pushmin_dq after pushShrink while spread > limitbest
08[0] (values 8)[0] (values 8)spread 0, no shrink1
12[0, 1] (values 8, 2)[1] (pops 0: 8 >= 2)spread 6 > 4: left -> 1, max_dq drops 0 -> [1]1
24[2] (pops 1: 2 <= 4)[1, 2] (values 2, 4)spread 2, no shrink2
37[3] (pops 2: 4 <= 7)[1, 2, 3] (values 2, 4, 7)spread 5 > 4: left -> 2, min_dq drops 1 -> [2, 3]2
Code Shape
Archetype Code Shape
Python
1max_dq, min_dq = deque(), deque()
2left = 0
3best = 0
4for right in range(len(nums)):
5 while max_dq and nums[max_dq[-1]] <= nums[right]:
6 max_dq.pop()
7 max_dq.append(right)
8 while min_dq and nums[min_dq[-1]] >= nums[right]:
9 min_dq.pop()
10 min_dq.append(right)
11 while nums[max_dq[0]] - nums[min_dq[0]] > limit:
12 left += 1
13 if max_dq[0] < left: max_dq.popleft()
14 if min_dq[0] < left: min_dq.popleft()
15 best = max(best, right - left + 1)
16return best

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

The Monotonic Deque card gives a window's maximum (or its minimum) in O(1) per step. This problem needs both the maximum and the minimum of the same window at once, because the window is only valid while they stay within limit of each other. Running one deque of each kind side by side, over the same two pointers, answers both questions together without rescanning the window.

⚖️ The Analogy: Two Leaderboards, Tallest and Shortest, Watching the Same Room

Keep two leaderboards for the same room of people: one for "tallest so far," one for "shortest so far," exactly like the single-deque leaderboard, run twice. Every time someone new walks in, both leaderboards update by their own rule. When someone leaves (the window shrinks), each leaderboard must check its own front on its own: the tallest-so-far leaderboard might still have a current leader standing in the room, while the shortest-so-far leaderboard's current leader already walked out. Checking only one leaderboard when someone leaves means the other can keep naming someone who isn't there anymore.

🔑 The Key Idea in the Code

nums[max_dq[0]] - nums[min_dq[0]] is the window's exact spread, read with no scan. While that spread is greater than limit, left moves forward one step, and both if max_dq[0] < left and if min_dq[0] < left run every time, because each deque only knows about the indices it has personally evicted.

  • Shrinking checks only max_dq's stale front, never min_dq's

  • Writing >= instead of <= when popping max_dq's back

  • Evicting a stale front on <= instead of <

  • Storing values instead of indices in max_dq and min_dq

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

The statement asks about every pair of elements inside a subarray, which is the same as its maximum minus its minimum. The window's validity depends on both running extremes at once, which a single Monotonic Deque cannot answer, since it only tracks one side.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

Decision at each right: keep max_dq strictly decreasing and min_dq strictly increasing, then while nums[max_dq[0]] - nums[min_dq[0]] > limit, move left forward and check max_dq[0] < left and min_dq[0] < left separately before recording right - left + 1.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • if max_dq[0] < left: ... if min_dq[0] < left: ...: both checks run on every shrink step, never only one (gotcha 1 has the failing input).

  • while max_dq and nums[max_dq[-1]] <= nums[right]:: keep the <= direction for the max deque, >= for the min deque, never swapped (gotcha 2).

  • if max_dq[0] < left:: strictly <, not <=, since index left itself is still inside the window (gotcha 3).

The 60-Second Interview Pitch

Say this out loud before you type a single line

"I'll use a Dual Monotonic Deque. I run two Monotonic Deques over the same window: max_dq strictly decreasing, so its front is the window's maximum, and min_dq strictly increasing, so its front is the window's minimum. For each right, I push right onto both deques after popping whatever it dominates. Then, while the window's spread, nums of max_dq's front minus nums of min_dq's front, is over the limit, I move left forward one step and check both fronts on their own: if max_dq's front fell behind left I pop it, and separately if min_dq's front fell behind left I pop it. The trap is checking only one of those two: each deque only evicts what it has itself checked, so one front can go stale before the other. After the window is valid again, I record right minus left plus one as a candidate length. Every index is pushed into each deque once and popped at most once, so it's O(N) time and O(N) space for the two deques."

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N)

Look at the code: for right in range(len(nums)) runs N times. Inside it, max_dq.append(right) and min_dq.append(right) are one push each, and the two while loops that pop from their backs (nums[max_dq[-1]] <= nums[right], nums[min_dq[-1]] >= nums[right]) can run many times in one step, but each pop removes an index that was appended earlier, and every index is appended exactly once into each deque, so across the whole run each deque's back pops total at most N. The shrink loop while nums[max_dq[0]] - nums[min_dq[0]] > limit moves left forward; left only increases and never exceeds right, and each of its if max_dq[0] < left / if min_dq[0] < left pops an index from the front of its own deque at most once, so these front pops also total at most N each across the whole run. Adding up: N pushes and at most N back-pops per deque, plus at most N front-pops per deque from the shrink loop: O(N) total, not O(N) per deque doubled into something larger, since every bound here is already linear in N.

SPACE COMPLEXITY

O(N)

Look at the allocations: max_dq and min_dq each hold only indices still inside the window nums[left..right], so together they never exceed 2N entries, still O(N). There is no recursion, and the answer best is one integer.

Formal Recurrence Relation

T(N)=2N⏟appends, both deques+≤2N⏟back pops+≤2N⏟front pops from shrinking=O(N)T(N) = \underbrace{2N}_{\text{appends, both deques}} + \underbrace{\le 2N}_{\text{back pops}} + \underbrace{\le 2N}_{\text{front pops from shrinking}} = O(N)T(N)=appends, both deques2N​​+back pops≤2N​​+front pops from shrinking≤2N​​=O(N)

Derivation Progression

Outer loop

for right in range(len(nums)): N passes, each pushing once into max_dq and once into min_dq

Everything outside the two inner while loops and the shrink loop costs a constant amount per index.

Back pops, counted over the whole run

total max_dq.pop() <= total max_dq.append(right) = N; same bound for min_dq

An index can only be popped from a deque's back after it was appended to that same deque, and each index is appended once per deque, so each deque's inner while pops at most N times across all passes together.

Front pops from shrinking, counted over the whole run

total max_dq.popleft() + total min_dq.popleft() <= N, since left only increases and left <= right <= N

left starts at 0 and only increases, and it never exceeds right, so it increases at most N times across the whole run; each increase pops at most one index from each deque's front.

Total

2N + 2N + 2N = O(N)

Appends, back pops and front pops on both deques together add up to a constant times N.

Variable Definitions

NNN

len(nums), the number of values

Memory Architecture & Bounds

🟣 Call Stack

O(1): no recursion

🔵 Auxiliary Heap

O(N): max_dq and min_dq together hold at most 2N indices inside the current window

🟢 Output Space

O(1): best, a single integer

Boundary Best / Worst Cases

Best Case

O(N)O(N)O(N): every value equal, nothing is ever popped from either deque's back, and the window never has to shrink

Average Case

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

Worst Case

O(N)O(N)O(N): an alternating high-low pattern, where every new value pops something from one deque's back and the window must shrink often

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: "every pair of elements in this subarray", "absolute difference... less than or equal to limit". The window's validity depends on both its maximum and its minimum at once: two Monotonic Deques, one of each kind, not a single deque and not a heap of pairs.

CONSTRAINTS & BOUNDS

1≤N≤1051 \le N \le 10^51≤N≤105, values in [1,109][1, 10^9][1,109]. Checking every pair inside a candidate subarray is O(N3)O(N^3)O(N3) in the worst case; a shrinking window with two deques is O(N)O(N)O(N), since each index is pushed once into each deque and popped from it at most once.

FAANG PRODUCTION TRAPS & EDGE CASES

THE SHARED-SHRINK TRAP: when the window shrinks, each deque's front is checked on its own if, never only one of them, because a deque only evicts the indices it has itself checked; skipping one check leaves a stale front that no longer belongs to the window.

Core Algorithmic State Invariants

  1. Two Deques, Two Independent Fronts

max_dq and min_dq each run the same Monotonic Deque rule on their own side, so each deque only ever evicts the indices it has itself checked. One deque's front can fall behind left before the other's does, which is why the shrink loop tests max_dq[0] and min_dq[0] separately, on every step, instead of assuming one check covers both.

  1. The Window's Spread, Read Without a Scan

nums[max_dq[0]] - nums[min_dq[0]] is the window's exact maximum minus its exact minimum, because both fronts are kept current by the same rule that keeps a single Monotonic Deque's front current. No element of the window is ever re-read to compute this.

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