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).

12 Core Algorithmic Patterns & 189 Practice Problems

  • 1. Two Pointers (10 Paradigms, 34 Problems): Converging pointers, sorted pair sums, container with most water, trapping rain water, 3Sum, plus the Sliding Window and Fast & Slow Pointers paradigms (Floyd cycle detection, monotonic window invariants, longest substrings, minimum window).
  • 2. Binary Search (8 Paradigms, 12 Problems): Monotonic predicate partitioning, boundary searching, rotated arrays, median of two sorted arrays, matrix median on value range.
  • 3. Bit Manipulation (5 Paradigms, 8 Problems): Bitmasking, XOR tricks, counting set bits, subset enumeration via bitmasks.
  • 4. Math & Geometry (7 Paradigms, 13 Problems): Sieve of Eratosthenes, integer/roman conversions, modular arithmetic, geometric simulation.
  • 5. Tree/Graph Depth-First Search (10 Paradigms, 19 Problems): Path sums, lowest common ancestor, tree diameter, subtree serialization, validating BSTs.
  • 6. Tree/Graph Breadth-First Search (4 Paradigms, 11 Problems): Level-order traversals, shortest path, rotting oranges, word ladders.
  • 7. Graphs (10 Paradigms, 18 Problems): Topological sort, cycle detection, Dijkstra, bipartite validation, network delay.
  • 8. Backtracking (6 Paradigms, 14 Problems): Subsets, permutations, combinations, constraint satisfaction, pruning, N-Queens.
  • 9. Dynamic Programming (11 Paradigms, 18 Problems): Memoization vs tabulation, knapsack, coin change, edit distance, longest common subsequence.
  • 10. Heap / Priority Queue (8 Paradigms, 10 Problems): Running medians, top-k elements, interval scheduling, IPO, k-way merges.
  • 11. Advanced Data Structures (6 Paradigms, 14 Problems): Trie, Union-Find, LRU Cache, LFU Cache, Monotonic Stacks.
  • 12. Intervals & Stack / Miscellaneous (11 Paradigms, 18 Problems): Merge intervals, daily temperatures, largest rectangle in histogram, trapping rain water via stack.

4-Stage Deliberate Practice Framework

  1. Stage 1 (Compare & Learn): Multi-language Rosetta Stone contrasting abstract invariants with concrete solutions across Python, C#, Java, TypeScript, C++, Go, and Rust.
  2. Stage 2 (Active Recall): Reconstruct algorithmic template invariants from memory with real-time feedback before looking at solutions.
  3. Stage 3 (Senior SWE AI Mock Coach): Simulated senior mock interview evaluating Big-O space/time tradeoffs, edge cases, and code reviews in Monaco Editor.
  4. Stage 4 (Solve on Your Own): Timed sandbox challenges verified against automated test suites in Python, C#, Java, and TypeScript.

Equipped with SM-2 Spaced Repetition Review Hub, Studio Cockpit workspace layout, and interactive study notes.

Pricing, Access & Commercial Terms

  • Core Curriculum: 100% Free. No credit card required.
  • Compute Coins: 40 free coins upon signup, +20 daily login bonus, +25 referral bonus.
  • 24-Hour AI Coaching Pass: 5 compute coins unlocks unlimited senior SWE AI coaching for a full 24 hours.
  • BYOK (Bring Your Own Key): Completely free unlimited AI coaching if using your own Gemini/OpenAI API key.
  • Refund & Subscription Policy: No recurring charges, no subscription traps, and no paid paywalls. Free tier provides full learning path.
  • Platform Operator: Hi👋WebEnterprise Inc. Support & policies at hispeedalgo.com.
Skip to main content
Hi👋SpeedAlgo

Invariant-First Algorithmic Mastery

201Items
Theory Context•Miscellaneous & Sweeps
HardLC 493

Reverse Pairs (LeetCode 493)

You will see how merge sort counts the pairs where an earlier value is more than twice a later one, in O(N log N).

Target Frequency:GoogleAmazonMeta

You get an integer array nums. A reverse pair is two positions i and j with i < j where the value at i is more than twice the value at j: nums[i] > 2 * nums[j].

Return how many reverse pairs nums holds.

Worked Examples

Example 1
Input:nums = [1,3,2,3,1]
Output:2
10312233143 > 23 > 2j
Explanation: The last value is `1`, and both `3`s (at positions 1 and 3) are more than twice it. No other pair works: `2 > 2 * 1` is false.
Example 2
Input:nums = [2,4,3,5,1]
Output:3
2041325314435j
Explanation: The last value is `1`, and `4`, `3` and `5` are each more than twice it. `2 > 2 * 1` is false, so `2` does not pair with it.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 5 * 104

  • -231 <= nums[i] <= 231 - 1

Deep-Dive & Conceptual Insights

Why It Works & Core Invariant

Every pair i < j crosses the middle of exactly one merge. At that moment both halves are sorted, so for each left value the right values it pairs with form a prefix of the right half, and that prefix only grows: one forward pointer counts all cross pairs in linear time.

Real-World Scenario & Production Applications

Comparing two rankings, spotting how far a feed is out of order, or flagging readings that dropped to less than half of an earlier one all count pairs "earlier and bigger than later". Checking every pair is O(N^2); counting them inside a merge sort takes O(N log N), because the sort does the ordering work the count needs.

Step-by-Step Execution Trace Table

The debugger's first preset, the trap case nums = [3,5,2,1] (answer 3):

CallHalves after sortingCross pairs counted before mergingcountMerged
sort_count(0, 2)[3], [5]3 > 2 * 5? no: j stays0[3, 5]
sort_count(2, 4)[2], [1]2 > 2 * 1? no0[1, 2]
sort_count(0, 4)[3, 5], [1, 2]i at 3: 3 > 2 * 1 yes, 3 > 2 * 2 no: j - mid = 1. i at 5: j stays past the 1; 5 > 2 * 2 yes: j - mid = 23[1, 2, 3, 5]
Scroll horizontally to see all columns, or expand to full screen

Counting inside the merge loop instead would visit the 2 while 3 is the waiting left value, see 3 > 4 fail, and never count 5 > 4: it returns 2.

Core Invariant Specification & Code Shape
Archetype Code Shape
Python
1Every pair `i < j` lies inside the left half, inside the right half, or crosses the middle; the recursive calls handle the first two kinds, so a call only counts pairs that cross its middle.
2Keep this true: when `sort_count(lo, hi)` returns, `nums[lo:hi]` is sorted and every pair inside it is counted. So when both calls return, both halves are sorted.
3The shape: a recursive function on `[lo, hi)`: return for one value; recurse on both halves; one pass over the left half with a second index into the right half that is never reset; then an ordinary merge written back into `nums[lo:hi]`.
4The trap: count in that separate pass, never inside the merge loop. The merge moves by `nums[a] <= nums[b]`, not by `nums[i] > 2 * nums[j]`, so on `[3,5,2,1]` counting while merging returns 2 instead of 3.

Target: Reverse Pairs (LeetCode 493). The base case of the split.

Boundary Model: Sorted Coordinate Sweep-Line & Monotonic Stack

Intervals sorted by start coordinate; monotonic stack preserves strictly increasing or decreasing element values.

Loop Invariant Termination

Sweep: if curr.start <= prev.end: merge; Stack: while stack and nums[i] >= stack[-1]: stack.pop().

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Checking every pair is O(N^2). Merge Sort Counting splits the work the way merge sort does. A pair i < j either sits inside the left half, inside the right half, or crosses the middle, with i on the left and j on the right. The first two kinds are counted by the recursive calls. The third kind is counted in sort_count itself, and here the sort pays off: both halves come back sorted, and a left value only needs the right values smaller than half of it. Those are a prefix of the sorted right half, and as nums[i] grows through the sorted left half, that prefix can only grow, so one pointer j moving forward counts every cross pair. Only then are the halves merged, so the parent sees a sorted range too.

🏃 The Analogy: Two Sorted Teams Comparing Scores

Two teams line up by score, lowest first. For each runner on the left team you want to know how many right-team runners scored less than half of them. You don't compare every pair: a flag walks along the right team, and for each left runner, taken in order, you push it forward past every right runner below half their score. A stronger left runner never needs the flag to go back. Then the teams merge into one sorted line, ready for the next round.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
j = mid
for i in range(lo, mid):
while j < hi and nums[i] > 2 * nums[j]:
j += 1
count += j - mid
 

Both halves are sorted when this loop runs, so nums[mid:j] is exactly the right values that pair with nums[i], and a bigger nums[i] only pushes j further: the whole count is O(N) per merge. The trap is counting inside the merge loop, the way inversions are counted: the merge moves by nums[a] <= nums[b], not by the doubled test, so a right value can be placed before every left value that pairs with it has been seen. On [3,5,2,1] that misses 5 > 2 * 2. Count in its own pass first, then merge.

💡 Summary

Recurse on both halves, count cross pairs with a forward-only j while both halves are sorted, then merge. log N levels of O(N) work each: O(Nlog⁡N)O(N \log N)O(NlogN) time, O(N)O(N)O(N) space for the merge buffer.

  • Counting inside the merge loop (the trap): the merge moves by nums[a] <= nums[b], not by nums[i] > 2 * nums[j], so the inversion-count step count += mid - a misses pairs. On [3,5,2,1] it returns 2 instead of 3; count in its own pass, then merge.

  • >= instead of >: equality is not a pair: [2, 1] has none.

  • Halving instead of doubling: nums[i] // 2 > nums[j] rounds odd and negative values the wrong way ([7, 3, -4, 3]). In Java or C++, 2 * nums[j] needs 64 bits: values reach 231 - 1.

  • Restarting the pointer: j = mid inside the for loop keeps the answer right but makes each merge quadratic.

  • Not writing the merge back: the parent's count needs nums[lo:hi] sorted; forgetting nums[lo:hi] = merged breaks every level above.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

You will see how a senior engineer spots a pair count over i < j and defends counting during merge sort out loud.

Pattern Recognition Signals

The 10-second spot

Count the positions "i < j" with a comparison between the two values, "nums[i] > 2 * nums[j]", on up to 5 * 104 values: every pair would be over a billion checks. A pair condition over positions that only needs the values sorted on each side is the signal for Merge Sort Counting.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

When sort_count(lo, hi) returns, nums[lo:hi] is sorted and all its pairs are counted. Between the recursive calls and the merge, both halves are sorted, so while j < hi and nums[i] > 2 * nums[j]: j += 1 and count += j - mid count the cross pairs of each nums[i], with j never moving back.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Count in its own pass, before the merge: counting inside the merge loop, as for inversions, misses pairs because the merge moves by nums[a] <= nums[b], not by the doubled test. On [3,5,2,1] it returns 2 instead of 3.

  • nums[i] > 2 * nums[j], strictly: >= counts [2, 1] as a pair.

  • Double, don't halve: nums[i] // 2 > nums[j] rounds wrong for odd and negative values, as in [7, 3, -4, 3]. In Java or C++, compute 2 * nums[j] in 64 bits.

  • Keep j outside the for loop: restarting it at mid for every i is still correct but makes the count quadratic.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Merge Sort Counting. Every pair with i before j either sits inside one half of the array or crosses the middle, so I sort and count each half recursively, and count only the crossing pairs myself. When the two halves come back, both are sorted. For a left value, the right values it pairs with are the ones below half of it, which is a prefix of the sorted right half, and that prefix only grows as the left values grow. So one pointer moves forward through the right half and I add its distance from the middle for each left value. Then I merge the halves so my parent gets a sorted range. The trap is counting inside the merge loop like inversions: the merge moves by plain order, not by the doubled condition, so it misses pairs. Each level is linear and there are log N levels: O(N log N) time, O(N) space.

So: split, count cross pairs with a forward-only pointer while both halves are sorted, then merge.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N log N)

Look at one call sort_count(lo, hi) with n = hi - lo values. The counting loop runs i over the left half and moves j forward only, so it does at most n steps of i and n / 2 steps of j: O(n). The merge takes each value once: O(n). So T(n) = 2 T(n / 2) + O(n). The recursion has log N levels, and the calls of one level cover all N values once, so each level is O(N). Total: O(N log N).

SPACE COMPLEXITY

O(N)

merged holds at most N values at the top call, and each call's buffer is freed before its parent builds its own, so the extra space is O(N). The recursion stack is log N calls deep, O(log N). The answer is one integer.

Formal Recurrence Relation

T(N) = 2 T(N / 2) + O(N) = O(N log N)

Look at one call sort_count(lo, hi) with n = hi - lo values. The counting loop runs i over the left half and moves j forward only, so it does at most n steps of i and n / 2 steps of j: O(n). The merge takes each value once: O(n). So T(n) = 2 T(n / 2) + O(n). The recursion has log N levels, and the calls of one level cover all N values once, so each level is O(N). Total: O(N log N).

Derivation Progression

Split

2 T(n / 2)

sort_count(lo, mid) and sort_count(mid, hi) on halves of size about n / 2.

Count cross pairs

O(n)

i walks the left half once and j only moves forward through the right half.

Merge

O(n)

Each value is appended to merged once and written back once.

Total

O(N log N)

log N levels, each covering N values with O(1) work per value.

Variable Definitions

NNN

Number of values, len(nums) (at most 5 * 104)

nnn

Size of the range of one call, hi - lo

Memory Architecture & Bounds

🟣 Call Stack

O(log N): the recursion depth

🔵 Auxiliary Heap

O(N): merged, at most N values at the top call

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(Nlog⁡N)O(N \log N)O(NlogN): every level merges all values

Average Case

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

Worst Case

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

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: **"i < j"**, **"nums[i] > 2 * nums[j]"**. Count pairs ordered by position that satisfy a comparison between their values, with n up to 5⋅1045 \cdot 10^45⋅104: Merge Sort Counting, counting the cross pairs of each merge.

CONSTRAINTS & BOUNDS

N≤5⋅104N \le 5 \cdot 10^4N≤5⋅104: all pairs are about 1.25⋅1091.25 \cdot 10^91.25⋅109 checks. Merge sort counting is O(Nlog⁡N)O(N \log N)O(NlogN), about 8⋅1058 \cdot 10^58⋅105 steps; a Fenwick tree over compressed values is also O(Nlog⁡N)O(N \log N)O(NlogN).

FAANG PRODUCTION TRAPS & EDGE CASES

Count in its own pass, never inside the merge loop. 2 * nums[j] overflows 32 bits in Java and C++ (use 64-bit or long); dividing instead, nums[i] / 2 > nums[j], rounds wrong for odd and negative values. The function sorts nums in place: copy it if the caller needs the original order. For data too big for memory, the same count runs as an external merge sort, one sorted run at a time.

Core Algorithmic State Invariants

1. Every Pair Crosses Exactly One Middle

A pair `i < j` lies inside one half, or crosses the middle of the call where `i` is on the left and `j` on the right: `count = sort_count(lo, mid) + sort_count(mid, hi)`, plus the cross pairs, counts each pair once.

2. Count Before You Merge

With both halves sorted, `while j < hi and nums[i] > 2 * nums[j]: j += 1` finds the prefix `nums[mid:j]` that pairs with `nums[i]`, and `j` never moves back. The merge moves by `nums[a] <= nums[b]`, so it can't count pairs defined by a different test.

3. Linear Work per Level

Counting and merging are O(hi - lo) per call and each level of the recursion covers N values once, with `log N` levels: O(N log N) time, O(N) for `merged`, O(log N) recursion stack.

Theory Context•Miscellaneous & Sweeps
HardLC 493

Reverse Pairs (LeetCode 493)

You will see how merge sort counts the pairs where an earlier value is more than twice a later one, in O(N log N).

Target Frequency:GoogleAmazonMeta

You get an integer array nums. A reverse pair is two positions i and j with i < j where the value at i is more than twice the value at j: nums[i] > 2 * nums[j].

Return how many reverse pairs nums holds.

Worked Examples

Example 1
Input:nums = [1,3,2,3,1]
Output:2
10312233143 > 23 > 2j
Explanation: The last value is `1`, and both `3`s (at positions 1 and 3) are more than twice it. No other pair works: `2 > 2 * 1` is false.
Example 2
Input:nums = [2,4,3,5,1]
Output:3
2041325314435j
Explanation: The last value is `1`, and `4`, `3` and `5` are each more than twice it. `2 > 2 * 1` is false, so `2` does not pair with it.

⚖️Formal Constraints & Bounds

  • 1 <= nums.length <= 5 * 104

  • -231 <= nums[i] <= 231 - 1

Deep-Dive & Conceptual Insights

Why It Works & Core Invariant

Every pair i < j crosses the middle of exactly one merge. At that moment both halves are sorted, so for each left value the right values it pairs with form a prefix of the right half, and that prefix only grows: one forward pointer counts all cross pairs in linear time.

Real-World Scenario & Production Applications

Comparing two rankings, spotting how far a feed is out of order, or flagging readings that dropped to less than half of an earlier one all count pairs "earlier and bigger than later". Checking every pair is O(N^2); counting them inside a merge sort takes O(N log N), because the sort does the ordering work the count needs.

Step-by-Step Execution Trace Table

The debugger's first preset, the trap case nums = [3,5,2,1] (answer 3):

CallHalves after sortingCross pairs counted before mergingcountMerged
sort_count(0, 2)[3], [5]3 > 2 * 5? no: j stays0[3, 5]
sort_count(2, 4)[2], [1]2 > 2 * 1? no0[1, 2]
sort_count(0, 4)[3, 5], [1, 2]i at 3: 3 > 2 * 1 yes, 3 > 2 * 2 no: j - mid = 1. i at 5: j stays past the 1; 5 > 2 * 2 yes: j - mid = 23[1, 2, 3, 5]
Scroll horizontally to see all columns, or expand to full screen

Counting inside the merge loop instead would visit the 2 while 3 is the waiting left value, see 3 > 4 fail, and never count 5 > 4: it returns 2.

Core Invariant Specification & Code Shape
Archetype Code Shape
Python
1Every pair `i < j` lies inside the left half, inside the right half, or crosses the middle; the recursive calls handle the first two kinds, so a call only counts pairs that cross its middle.
2Keep this true: when `sort_count(lo, hi)` returns, `nums[lo:hi]` is sorted and every pair inside it is counted. So when both calls return, both halves are sorted.
3The shape: a recursive function on `[lo, hi)`: return for one value; recurse on both halves; one pass over the left half with a second index into the right half that is never reset; then an ordinary merge written back into `nums[lo:hi]`.
4The trap: count in that separate pass, never inside the merge loop. The merge moves by `nums[a] <= nums[b]`, not by `nums[i] > 2 * nums[j]`, so on `[3,5,2,1]` counting while merging returns 2 instead of 3.

Target: Reverse Pairs (LeetCode 493). The base case of the split.

Boundary Model: Sorted Coordinate Sweep-Line & Monotonic Stack

Intervals sorted by start coordinate; monotonic stack preserves strictly increasing or decreasing element values.

Loop Invariant Termination

Sweep: if curr.start <= prev.end: merge; Stack: while stack and nums[i] >= stack[-1]: stack.pop().

Conceptual Narrative

🧭 Conceptual Foundation & Pattern Intuition

Checking every pair is O(N^2). Merge Sort Counting splits the work the way merge sort does. A pair i < j either sits inside the left half, inside the right half, or crosses the middle, with i on the left and j on the right. The first two kinds are counted by the recursive calls. The third kind is counted in sort_count itself, and here the sort pays off: both halves come back sorted, and a left value only needs the right values smaller than half of it. Those are a prefix of the sorted right half, and as nums[i] grows through the sorted left half, that prefix can only grow, so one pointer j moving forward counts every cross pair. Only then are the halves merged, so the parent sees a sorted range too.

🏃 The Analogy: Two Sorted Teams Comparing Scores

Two teams line up by score, lowest first. For each runner on the left team you want to know how many right-team runners scored less than half of them. You don't compare every pair: a flag walks along the right team, and for each left runner, taken in order, you push it forward past every right runner below half their score. A stronger left runner never needs the flag to go back. Then the teams merge into one sorted line, ready for the next round.

🪄 The Mathematical Harmony / Magic Trick
Code / Blueprint
j = mid
for i in range(lo, mid):
while j < hi and nums[i] > 2 * nums[j]:
j += 1
count += j - mid
 

Both halves are sorted when this loop runs, so nums[mid:j] is exactly the right values that pair with nums[i], and a bigger nums[i] only pushes j further: the whole count is O(N) per merge. The trap is counting inside the merge loop, the way inversions are counted: the merge moves by nums[a] <= nums[b], not by the doubled test, so a right value can be placed before every left value that pairs with it has been seen. On [3,5,2,1] that misses 5 > 2 * 2. Count in its own pass first, then merge.

💡 Summary

Recurse on both halves, count cross pairs with a forward-only j while both halves are sorted, then merge. log N levels of O(N) work each: O(Nlog⁡N)O(N \log N)O(NlogN) time, O(N)O(N)O(N) space for the merge buffer.

  • Counting inside the merge loop (the trap): the merge moves by nums[a] <= nums[b], not by nums[i] > 2 * nums[j], so the inversion-count step count += mid - a misses pairs. On [3,5,2,1] it returns 2 instead of 3; count in its own pass, then merge.

  • >= instead of >: equality is not a pair: [2, 1] has none.

  • Halving instead of doubling: nums[i] // 2 > nums[j] rounds odd and negative values the wrong way ([7, 3, -4, 3]). In Java or C++, 2 * nums[j] needs 64 bits: values reach 231 - 1.

  • Restarting the pointer: j = mid inside the for loop keeps the answer right but makes each merge quadratic.

  • Not writing the merge back: the parent's count needs nums[lo:hi] sorted; forgetting nums[lo:hi] = merged breaks every level above.

Senior SWE Reasoning Architecture

4-Phase Thought Process Model

You will see how a senior engineer spots a pair count over i < j and defends counting during merge sort out loud.

Pattern Recognition Signals

The 10-second spot

Count the positions "i < j" with a comparison between the two values, "nums[i] > 2 * nums[j]", on up to 5 * 104 values: every pair would be over a billion checks. A pair condition over positions that only needs the values sorted on each side is the signal for Merge Sort Counting.

Formulating the Predicate & Invariants

Turning intuition into a boolean rule

When sort_count(lo, hi) returns, nums[lo:hi] is sorted and all its pairs are counted. Between the recursive calls and the merge, both halves are sorted, so while j < hi and nums[i] > 2 * nums[j]: j += 1 and count += j - mid count the cross pairs of each nums[i], with j never moving back.

Silent Failure Traps & Edge Cases

Where confident candidates still lose points

  • Count in its own pass, before the merge: counting inside the merge loop, as for inversions, misses pairs because the merge moves by nums[a] <= nums[b], not by the doubled test. On [3,5,2,1] it returns 2 instead of 3.

  • nums[i] > 2 * nums[j], strictly: >= counts [2, 1] as a pair.

  • Double, don't halve: nums[i] // 2 > nums[j] rounds wrong for odd and negative values, as in [7, 3, -4, 3]. In Java or C++, compute 2 * nums[j] in 64 bits.

  • Keep j outside the for loop: restarting it at mid for every i is still correct but makes the count quadratic.

The 60-Second Interview Pitch

Say this out loud before you type a single line

I'd use Merge Sort Counting. Every pair with i before j either sits inside one half of the array or crosses the middle, so I sort and count each half recursively, and count only the crossing pairs myself. When the two halves come back, both are sorted. For a left value, the right values it pairs with are the ones below half of it, which is a prefix of the sorted right half, and that prefix only grows as the left values grow. So one pointer moves forward through the right half and I add its distance from the middle for each left value. Then I merge the halves so my parent gets a sorted range. The trap is counting inside the merge loop like inversions: the merge moves by plain order, not by the doubled condition, so it misses pairs. Each level is linear and there are log N levels: O(N log N) time, O(N) space.

So: split, count cross pairs with a forward-only pointer while both halves are sorted, then merge.

Big-O Invariant Derivation

Complexity & Mathematical Proof

TIME COMPLEXITY

O(N log N)

Look at one call sort_count(lo, hi) with n = hi - lo values. The counting loop runs i over the left half and moves j forward only, so it does at most n steps of i and n / 2 steps of j: O(n). The merge takes each value once: O(n). So T(n) = 2 T(n / 2) + O(n). The recursion has log N levels, and the calls of one level cover all N values once, so each level is O(N). Total: O(N log N).

SPACE COMPLEXITY

O(N)

merged holds at most N values at the top call, and each call's buffer is freed before its parent builds its own, so the extra space is O(N). The recursion stack is log N calls deep, O(log N). The answer is one integer.

Formal Recurrence Relation

T(N) = 2 T(N / 2) + O(N) = O(N log N)

Look at one call sort_count(lo, hi) with n = hi - lo values. The counting loop runs i over the left half and moves j forward only, so it does at most n steps of i and n / 2 steps of j: O(n). The merge takes each value once: O(n). So T(n) = 2 T(n / 2) + O(n). The recursion has log N levels, and the calls of one level cover all N values once, so each level is O(N). Total: O(N log N).

Derivation Progression

Split

2 T(n / 2)

sort_count(lo, mid) and sort_count(mid, hi) on halves of size about n / 2.

Count cross pairs

O(n)

i walks the left half once and j only moves forward through the right half.

Merge

O(n)

Each value is appended to merged once and written back once.

Total

O(N log N)

log N levels, each covering N values with O(1) work per value.

Variable Definitions

NNN

Number of values, len(nums) (at most 5 * 104)

nnn

Size of the range of one call, hi - lo

Memory Architecture & Bounds

🟣 Call Stack

O(log N): the recursion depth

🔵 Auxiliary Heap

O(N): merged, at most N values at the top call

🟢 Output Space

O(1): one integer

Boundary Best / Worst Cases

Best Case

O(Nlog⁡N)O(N \log N)O(NlogN): every level merges all values

Average Case

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

Worst Case

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

Recurrence Tree Topology

Recurrence Tree Topology
Synthesizing vector architecture diagram...
Staff+ Engineering Perspective

Senior SWE Deconstruction & Hardware Caveats

ARCHITECTURAL SIGNALS & INTERVIEW TRIGGERS

Triggers: **"i < j"**, **"nums[i] > 2 * nums[j]"**. Count pairs ordered by position that satisfy a comparison between their values, with n up to 5⋅1045 \cdot 10^45⋅104: Merge Sort Counting, counting the cross pairs of each merge.

CONSTRAINTS & BOUNDS

N≤5⋅104N \le 5 \cdot 10^4N≤5⋅104: all pairs are about 1.25⋅1091.25 \cdot 10^91.25⋅109 checks. Merge sort counting is O(Nlog⁡N)O(N \log N)O(NlogN), about 8⋅1058 \cdot 10^58⋅105 steps; a Fenwick tree over compressed values is also O(Nlog⁡N)O(N \log N)O(NlogN).

FAANG PRODUCTION TRAPS & EDGE CASES

Count in its own pass, never inside the merge loop. 2 * nums[j] overflows 32 bits in Java and C++ (use 64-bit or long); dividing instead, nums[i] / 2 > nums[j], rounds wrong for odd and negative values. The function sorts nums in place: copy it if the caller needs the original order. For data too big for memory, the same count runs as an external merge sort, one sorted run at a time.

Core Algorithmic State Invariants

1. Every Pair Crosses Exactly One Middle

A pair `i < j` lies inside one half, or crosses the middle of the call where `i` is on the left and `j` on the right: `count = sort_count(lo, mid) + sort_count(mid, hi)`, plus the cross pairs, counts each pair once.

2. Count Before You Merge

With both halves sorted, `while j < hi and nums[i] > 2 * nums[j]: j += 1` finds the prefix `nums[mid:j]` that pairs with `nums[i]`, and `j` never moves back. The merge moves by `nums[a] <= nums[b]`, so it can't count pairs defined by a different test.

3. Linear Work per Level

Counting and merging are O(hi - lo) per call and each level of the recursion covers N values once, with `log N` levels: O(N log N) time, O(N) for `merged`, O(log N) recursion stack.

Rosetta Dual-Monaco Comparison
Python 3
CANONICAL INVARIANT TEMPLATE
Loading...
CONCRETE: REVERSE PAIRS (LEETCODE 493)
T = O(N log N)S = O(N)
Loading...
Pattern Implementation Mapping TableCanonical Invariant ⟷ Concrete Code ⟷ Engineering Rationale
Canonical InvariantConcrete CodeEngineering Rationale
One value holds no pairif hi - lo <= 1: return 0The base case of the split.
Count inside each half; each comes back sortedcount = sort_count(lo, mid) + sort_count(mid, hi)Pairs that don't cross the middle belong to exactly one half.
A forward-only pointer into the sorted right halfwhile j < hi and nums[i] > 2 * nums[j]: j += 1The pair condition picks a prefix of the right half, and a bigger left value only lengthens it.
Count the cross pairs of this left valuecount += j - mid`nums[mid:j]` all pair with `nums[i]`.
Merge only after countingif nums[a] <= nums[b]: merged.append(nums[a]) a += 1The trap: the merge moves by plain order, not by the pair condition, so it can't do the counting.
Hand a sorted range to the parentnums[lo:hi] = mergedThe parent's count relies on this range being sorted.
© 2026 Hi👋WebEnterprise. All rights reserved.
Sitemap•llms.txt•