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•Priority Queue / Heap
HardLC 630

Course Schedule III (LeetCode 630)

Target Frequency:GoogleAmazonMicrosoft

You are offered n online courses. courses[i] = [duration_i, lastDay_i]: course i takes duration_i days in a row, and it has to be finished no later than day lastDay_i. You start on day 1 and can only follow one course at a time, so the courses you choose run back to back.

Return the largest number of courses you can complete.

Worked Examples

Example 1
Input:courses = [[100,200],[200,1300],[1000,1250],[2000,3200]]
Output:3
[100,200][1000,1250][200,1300]11300
Explanation: Take the 100-day course (done on day 100), then the 1000-day course (done on day 1100), then the 200-day course (done on day 1300). The 2000-day course would end on day 3300, after its last day 3200.
Example 2
Input:courses = [[1,2]]
Output:1
Explanation: The only course ends on day 1, before its last day 2.
Example 3
Input:courses = [[3,2],[4,3]]
Output:0
Explanation: Each course is longer than the days available before its last day, so none can be finished.

⚖️Formal Constraints & Bounds

  • 1 <= courses.length <= 104

  • 1 <= duration_i, lastDay_i <= 104

Deep-Dive & Conceptual Insights

Core Insight

Go through the courses by last day and take each one; when the total runs past the current last day, dropping the longest course taken keeps the same count and frees the most days for what comes next.

Real-World Scenario & Production Applications

Admitting jobs into a queue where each job has a length and a due time: a scheduler that accepts every job, and evicts the longest accepted one whenever a due time would be missed, keeps the number of on-time jobs as high as possible. The same rule appears in classic single-machine scheduling (maximize the number of on-time jobs).

Step-by-Step Execution Trace Table

Input courses = [[100,200],[200,1300],[1000,1250],[2000,3200]], sorted by last day: [100,200], [1000,1250], [200,1300], [2000,3200].

StepCourse [duration, last_day]total_time after taking ittotal_time > last_day?Actionmax_heap (durations taken)
1[100, 200]100NoKeep{100}
2[1000, 1250]1100NoKeep{1000, 100}
3[200, 1300]1300No (equal is fine)Keep{1000, 200, 100}
4[2000, 3200]3300YesPop the longest, 2000: total_time = 1300{1000, 200, 100}
Endreturn len(max_heap) = 3
Code Shape
Archetype Code Shape
Python
1Take every course, and when the days run out, undo the single worst choice made so far: the longest course taken.
2`max_heap` always holds the most courses that fit the deadlines seen so far, with the smallest possible `total_time`.
3Sort `courses` by last day; for each `duration, last_day`: add `duration` to `total_time`, push `-duration`; if `total_time > last_day`, pop the longest and subtract it; `return len(max_heap)`.
4The trap: sort by `last_day`, not by `duration`, or a short course with a late deadline pushes out a course whose early deadline was still reachable.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Some greedy problems can't be solved by picking the right item up front, because you only learn later that an earlier pick was a mistake. Greedy with Regret takes every item as it comes, keeps the picks in a heap, and the moment a limit is broken it undoes the single worst pick. For Course Schedule III: go through courses by deadline, take each one, and if the running total of days now misses the current deadline, drop the longest course taken so far.

🏟️ The Analogy: Packing a Suitcase Against a Weight Limit

You pack items in the order you need them, and at each checkpoint the bag must be under the limit. Whenever you are over, you don't unpack everything and start again: you pull out the single heaviest item. The count of items stays as high as it can be, and the bag is as light as it can be, which leaves the most room for what comes next.

🔑 Why It Works

After each course, max_heap holds the most courses that fit the deadlines seen so far, and among all such sets the one with the smallest total_time. Dropping the longest course keeps the count and frees the most days, so no later course is ever blocked by a choice we could have made better.

  • Sorting by duration instead of last_day: shortest-first ignores deadlines. On [[3,3],[1,3],[1,6],[2,3]] shortest-first takes the two 1-day courses, then the 2-day course misses day 3 and gets dropped with the 3-day one: 2 courses instead of 3.

  • Checking before taking: if total_time + duration <= last_day: take it never revisits old picks, so one long early course can block several short later ones. Take the course first, then drop the longest if the deadline breaks.

  • Dropping the course just added instead of the longest: the new course may be short; dropping the longest one frees the most days for later courses while keeping the same count.

  • Returning total_time: the question asks how many courses; the answer is len(max_heap).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"The largest number of courses", each with a length and a last day, done one at a time: you pick as many items as possible under deadlines, and a pick that looked fine early can block several later ones. That is the signal to take greedily and keep a heap to undo the worst pick: Greedy with Regret.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After each course, max_heap holds the most courses that all finish by their last days, and among all sets of that size it has the smallest total_time; the rule is: push -duration, and if total_time > last_day, pop the longest and subtract it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • courses.sort(key=lambda c: c[1]) on [[3,3],[1,3],[1,6],[2,3]]: sorted by last day the answer is 3; sorted by duration it is 2, because the two 1-day courses go first and the 2-day and 3-day courses both miss day 3.

  • if total_time > last_day must be strict: finishing exactly on the last day is on time. In Example 1 the third course taken ends exactly on day 1300 and stays.

  • longest = -heapq.heappop(max_heap): Python's heap is a min-heap, so durations are stored negated; forgetting the minus pops the shortest course instead of the longest.

  • A course with duration > last_day can never fit: it is taken, pushed, and popped again at once (Example 3, [[3,2],[4,3]], returns 0), so no special case is needed.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Greedy with Regret. I sort the courses by their last day and walk through them, taking every course: I add its duration to a running total and push it onto a max-heap. If the total now passes this course's last day, I pop the longest course I've taken and subtract it. That keeps the count the same as before this course but makes the total as small as possible, so every later course has the most room to fit; and since all earlier courses have earlier deadlines, they still fit too. The trap is the sort key: it must be the last day, not the duration, or a short course with a late deadline can crowd out one whose early deadline was reachable. The answer is the heap's size. Sorting and the heap give O(N log N) time and O(N) space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N log N)

Look at the code: courses.sort(key=lambda c: c[1]) costs O(N log N). The loop runs once per course; each iteration does one heappush (O(log N)) and at most one heappop (O(log N)) on a heap that never holds more than N items. Total: O(N log N) + N · O(log N) = O(N log N).

SPACE COMPLEXITY

O(N)

max_heap holds at most N negated durations: O(N). Python's sort needs up to O(N) extra memory as well. The answer is one integer.

Formal Recurrence Relation

T(N) = O(N log N) + N · (O(log N) + O(log N)) = O(N log N)

Derivation Progression

Sort by last day

O(N log N)

courses.sort(key=lambda c: c[1]).

Take each course

N × O(log N)

One heappush per course onto a heap of at most N items.

Regret pops

at most N × O(log N)

Each course is popped at most once, since it is pushed once.

Total

O(N log N)

The sort and the heap work have the same order.

Variable Definitions

NNN

Number of courses, len(courses)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion

🔵 Auxiliary Heap

O(N): max_heap plus the sort's working memory

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(Nlog⁡N)O(N \log N)O(NlogN): the sort runs even if no course is ever popped

Average Case

O(Nlog⁡N)O(N \log N)O(NlogN)

Worst Case

O(Nlog⁡N)O(N \log N)O(NlogN)

Binary Heap Priority Queue Tree

Binary Heap Priority Queue Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the largest number of courses", "finished no later than day lastDay", "one course at a time". Count-maximizing selection under deadlines, where an early pick can block later ones: Greedy with Regret (take everything, undo the longest with a max-heap).

CONSTRAINTS & BOUNDS

N≤104N \le 10^4N≤104 courses, durations and last days ≤104\le 10^4≤104. Brute force over subsets is 2N2^N2N; the budget is O(Nlog⁡N)O(N \log N)O(NlogN) time and O(N)O(N)O(N) space.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale, the heap holds every accepted item; if items arrive as a stream sorted by due time, the same loop runs online, but items that arrive out of due-time order break the invariant, so buffer and sort them first.

Theory Context•Priority Queue / Heap
HardLC 630

Course Schedule III (LeetCode 630)

Target Frequency:GoogleAmazonMicrosoft

You are offered n online courses. courses[i] = [duration_i, lastDay_i]: course i takes duration_i days in a row, and it has to be finished no later than day lastDay_i. You start on day 1 and can only follow one course at a time, so the courses you choose run back to back.

Return the largest number of courses you can complete.

Worked Examples

Example 1
Input:courses = [[100,200],[200,1300],[1000,1250],[2000,3200]]
Output:3
[100,200][1000,1250][200,1300]11300
Explanation: Take the 100-day course (done on day 100), then the 1000-day course (done on day 1100), then the 200-day course (done on day 1300). The 2000-day course would end on day 3300, after its last day 3200.
Example 2
Input:courses = [[1,2]]
Output:1
Explanation: The only course ends on day 1, before its last day 2.
Example 3
Input:courses = [[3,2],[4,3]]
Output:0
Explanation: Each course is longer than the days available before its last day, so none can be finished.

⚖️Formal Constraints & Bounds

  • 1 <= courses.length <= 104

  • 1 <= duration_i, lastDay_i <= 104

Deep-Dive & Conceptual Insights

Core Insight

Go through the courses by last day and take each one; when the total runs past the current last day, dropping the longest course taken keeps the same count and frees the most days for what comes next.

Real-World Scenario & Production Applications

Admitting jobs into a queue where each job has a length and a due time: a scheduler that accepts every job, and evicts the longest accepted one whenever a due time would be missed, keeps the number of on-time jobs as high as possible. The same rule appears in classic single-machine scheduling (maximize the number of on-time jobs).

Step-by-Step Execution Trace Table

Input courses = [[100,200],[200,1300],[1000,1250],[2000,3200]], sorted by last day: [100,200], [1000,1250], [200,1300], [2000,3200].

StepCourse [duration, last_day]total_time after taking ittotal_time > last_day?Actionmax_heap (durations taken)
1[100, 200]100NoKeep{100}
2[1000, 1250]1100NoKeep{1000, 100}
3[200, 1300]1300No (equal is fine)Keep{1000, 200, 100}
4[2000, 3200]3300YesPop the longest, 2000: total_time = 1300{1000, 200, 100}
Endreturn len(max_heap) = 3
Code Shape
Archetype Code Shape
Python
1Take every course, and when the days run out, undo the single worst choice made so far: the longest course taken.
2`max_heap` always holds the most courses that fit the deadlines seen so far, with the smallest possible `total_time`.
3Sort `courses` by last day; for each `duration, last_day`: add `duration` to `total_time`, push `-duration`; if `total_time > last_day`, pop the longest and subtract it; `return len(max_heap)`.
4The trap: sort by `last_day`, not by `duration`, or a short course with a late deadline pushes out a course whose early deadline was still reachable.

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Some greedy problems can't be solved by picking the right item up front, because you only learn later that an earlier pick was a mistake. Greedy with Regret takes every item as it comes, keeps the picks in a heap, and the moment a limit is broken it undoes the single worst pick. For Course Schedule III: go through courses by deadline, take each one, and if the running total of days now misses the current deadline, drop the longest course taken so far.

🏟️ The Analogy: Packing a Suitcase Against a Weight Limit

You pack items in the order you need them, and at each checkpoint the bag must be under the limit. Whenever you are over, you don't unpack everything and start again: you pull out the single heaviest item. The count of items stays as high as it can be, and the bag is as light as it can be, which leaves the most room for what comes next.

🔑 Why It Works

After each course, max_heap holds the most courses that fit the deadlines seen so far, and among all such sets the one with the smallest total_time. Dropping the longest course keeps the count and frees the most days, so no later course is ever blocked by a choice we could have made better.

  • Sorting by duration instead of last_day: shortest-first ignores deadlines. On [[3,3],[1,3],[1,6],[2,3]] shortest-first takes the two 1-day courses, then the 2-day course misses day 3 and gets dropped with the 3-day one: 2 courses instead of 3.

  • Checking before taking: if total_time + duration <= last_day: take it never revisits old picks, so one long early course can block several short later ones. Take the course first, then drop the longest if the deadline breaks.

  • Dropping the course just added instead of the longest: the new course may be short; dropping the longest one frees the most days for later courses while keeping the same count.

  • Returning total_time: the question asks how many courses; the answer is len(max_heap).

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

Pattern Recognition Signals

The 10-second spot

"The largest number of courses", each with a length and a last day, done one at a time: you pick as many items as possible under deadlines, and a pick that looked fine early can block several later ones. That is the signal to take greedily and keep a heap to undo the worst pick: Greedy with Regret.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

After each course, max_heap holds the most courses that all finish by their last days, and among all sets of that size it has the smallest total_time; the rule is: push -duration, and if total_time > last_day, pop the longest and subtract it.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • courses.sort(key=lambda c: c[1]) on [[3,3],[1,3],[1,6],[2,3]]: sorted by last day the answer is 3; sorted by duration it is 2, because the two 1-day courses go first and the 2-day and 3-day courses both miss day 3.

  • if total_time > last_day must be strict: finishing exactly on the last day is on time. In Example 1 the third course taken ends exactly on day 1300 and stays.

  • longest = -heapq.heappop(max_heap): Python's heap is a min-heap, so durations are stored negated; forgetting the minus pops the shortest course instead of the longest.

  • A course with duration > last_day can never fit: it is taken, pushed, and popped again at once (Example 3, [[3,2],[4,3]], returns 0), so no special case is needed.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Greedy with Regret. I sort the courses by their last day and walk through them, taking every course: I add its duration to a running total and push it onto a max-heap. If the total now passes this course's last day, I pop the longest course I've taken and subtract it. That keeps the count the same as before this course but makes the total as small as possible, so every later course has the most room to fit; and since all earlier courses have earlier deadlines, they still fit too. The trap is the sort key: it must be the last day, not the duration, or a short course with a late deadline can crowd out one whose early deadline was reachable. The answer is the heap's size. Sorting and the heap give O(N log N) time and O(N) space.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N log N)

Look at the code: courses.sort(key=lambda c: c[1]) costs O(N log N). The loop runs once per course; each iteration does one heappush (O(log N)) and at most one heappop (O(log N)) on a heap that never holds more than N items. Total: O(N log N) + N · O(log N) = O(N log N).

SPACE COMPLEXITY

O(N)

max_heap holds at most N negated durations: O(N). Python's sort needs up to O(N) extra memory as well. The answer is one integer.

Formal Recurrence Relation

T(N) = O(N log N) + N · (O(log N) + O(log N)) = O(N log N)

Derivation Progression

Sort by last day

O(N log N)

courses.sort(key=lambda c: c[1]).

Take each course

N × O(log N)

One heappush per course onto a heap of at most N items.

Regret pops

at most N × O(log N)

Each course is popped at most once, since it is pushed once.

Total

O(N log N)

The sort and the heap work have the same order.

Variable Definitions

NNN

Number of courses, len(courses)

Memory Architecture & Bounds

🟣 Call Stack

O(1) Iterative, no recursion

🔵 Auxiliary Heap

O(N): max_heap plus the sort's working memory

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(Nlog⁡N)O(N \log N)O(NlogN): the sort runs even if no course is ever popped

Average Case

O(Nlog⁡N)O(N \log N)O(NlogN)

Worst Case

O(Nlog⁡N)O(N \log N)O(NlogN)

Binary Heap Priority Queue Tree

Binary Heap Priority Queue Tree
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: "the largest number of courses", "finished no later than day lastDay", "one course at a time". Count-maximizing selection under deadlines, where an early pick can block later ones: Greedy with Regret (take everything, undo the longest with a max-heap).

CONSTRAINTS & BOUNDS

N≤104N \le 10^4N≤104 courses, durations and last days ≤104\le 10^4≤104. Brute force over subsets is 2N2^N2N; the budget is O(Nlog⁡N)O(N \log N)O(NlogN) time and O(N)O(N)O(N) space.

FAANG PRODUCTION TRAPS & EDGE CASES

At scale, the heap holds every accepted item; if items arrive as a stream sorted by due time, the same loop runs online, but items that arrive out of due-time order break the invariant, so buffer and sort them first.

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