Coding Interview Patterns Module 7 – Sorting as a tool
Counting pairs with merge sort
Count inversions and reverse pairs, where an earlier value is more than twice a later one, in O(n log n) with merge sort, and compare a Fenwick tree.
What you will learn
- Count inversions during the merge step of merge sort
- Count pairs with a[i] > 2·a[j] for i < j in O(n log n) time
- Prove that every pair is counted exactly once
- Store counts and doubled values in 64-bit integers in every language
- Compare the merge-sort count with a Fenwick-tree count on the same data
Before you start
What you’ll learn · Before you start
What you will learn
- Count inversions during the merge step of merge sort
- Count pairs with a[i] > 2·a[j] for i < j in O(n log n) time
- Prove that every pair is counted exactly once
- Store counts and doubled values in 64-bit integers in every language
- Compare the merge-sort count with a Fenwick-tree count on the same data
Before you start
On this page
Merge sort can count pairs while it sorts. Many counting questions ask how many pairs i < j satisfy a condition
between an earlier and a later value: inversions, where a[i] > a[j], or reverse pairs, where a[i] > 2 · a[j].
Trying every pair costs O(n²). Merge sort splits the array, sorts both halves and merges them, and at every merge
both halves are sorted, so the pairs that cross the middle can be counted with pointers that only move forward: O(n)
per level and O(n log n) in all. This lesson’s problem counts reverse pairs in a trading log, where they are called
big drops.
The problem: big drops in a profit-and-loss log
The problem
A trading desk logs each day’s profit or loss in rupees, pnl[0..n − 1], in order; losses are negative. A pair
of days i < j is a big drop when pnl[i] > 2 · pnl[j]. Count the big drops. Limits: 0 ≤ n ≤ 10⁵ and
−2³¹ ≤ pnl[i] ≤ 2³¹ − 1.
[3, 1, 4, 1, 5]gives 3: day 0 with day 1, day 0 with day 3, and day 2 with day 3.[-2, -5, 1, -3]gives 4. The comparison is signed, so −2 > −10 makes days 0 and 1 a big drop.[2147483647, 1073741824]gives 0, because twice the second figure is 2,147,483,648, which a 32-bit integer cannot hold.
The three big drops of the first example
Text description of the diagram
Five boxes in a row hold the first example's figures, day 0 to day 4: 3, 1, 4, 1 and 5.
Arcs above the boxes join each big drop, an earlier day whose figure is more than twice a later day's: day 0 (3) to day 1 (1), labelled 3 > 2; day 0 (3) to day 3 (1), labelled 3 > 2; and day 2 (4) to day 3 (1), labelled 4 > 2. The line under the boxes notes that there are 3 big drops, and that 4 and the 1 of day 1 do not form one, because day 1 comes before day 2.
The brute force: every pair
Check every pair i < j. That is n(n − 1)/2 checks, correct by construction and the referee for faster code, but 4,999,950,000 checks at n = 10⁵:
# Big drops by brute force: check every pair of days i < j for pnl[i] > 2 * pnl[j]. Then how many pairs that is.
import random
def big_drops_brute(pnl):
"""Counts the pairs i < j with pnl[i] > 2 * pnl[j], and the pairs checked: n(n - 1)/2 of them."""
count = checked = 0
for j in range(len(pnl)):
for i in range(j):
checked += 1
if pnl[i] > 2 * pnl[j]:
count += 1
return count, checked
for pnl in ([3, 1, 4, 1, 5], [-2, -5, 1, -3], [2147483647, 1073741824]):
print(pnl, "->", big_drops_brute(pnl)[0])
rng = random.Random(6)
for n in (1_000, 2_000):
days = [rng.randint(-2**31, 2**31 - 1) for _ in range(n)]
count, checked = big_drops_brute(days)
print(f"n = {n:,}: {checked:,} pairs checked, {count:,} big drops")
n = 100_000
print(f"n = {n:,}: {n * (n - 1) // 2:,} pairs to check") Output
[3, 1, 4, 1, 5] -> 3 [-2, -5, 1, -3] -> 4 [2147483647, 1073741824] -> 0 n = 1,000: 499,500 pairs checked, 255,924 big drops n = 2,000: 1,999,000 pairs checked, 952,639 big drops n = 100,000: 4,999,950,000 pairs to check
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 drops_brute.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The run makes 499,500 checks for 1,000 days and 1,999,000 for 2,000: twice the days, four times the work. On 1,000 random days the log already holds 255,924 big drops, so the answer itself is large, and for 10⁵ days it can pass what a 32-bit integer holds.
What to notice
The condition compares an earlier value with a later one, and the count does not depend on how the values on each side are ordered among themselves. If the first half and the second half of the log were each sorted, then as the left value grows, the right values below half of it can only grow in number, so one sweep with two pointers would count every big drop that crosses the middle. Merge sort produces exactly those sorted halves, at every level.
Counting while merge sort sorts Pro: opens with a Pro, Premium or Ultimate pass
In this part:
- Why big drops need their own pass
- Every pair is counted exactly once: the proof
- A dry run on the first example
- The code in Python, JavaScript, Java, C++ and Go
- Overflow drills: the count and the doubling
- Three slips, caught by a stress test
- A Fenwick tree instead of merge sort, measured
- Edge cases and common bugs
- Variants, with answers
- Follow-up questions, answered
- Exercise and practice set
- Count the big drops in a profit-and-loss log
- Check yourself
- Interview questions
- Two recommendation models each rank the same 10⁵ products. The team wants one number for how much the rankings disagree: the count of product pairs that the two models order differently. Compute it in O(n log n), and say how large it can get.
- A candidate counts pairs i < j with a[i] > 2·a[j] by adding mid − i inside the merge whenever a[i] > 2·a[j]. It passes the given examples but fails hidden tests. Find an input where it fails, explain why, and fix it without losing O(n log n).
- For each of 10⁵ exam scores in submission order, report how many later scores are strictly lower. Give an O(n log n) method, compare merge sort with a Fenwick tree, and handle repeated scores.
- Key takeaways
1 diagram · 4 runnable examples (Build and run all four, C++, Go, Java (BigDrops.java), Java (Overflow.java), JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
References
- Dictionary of Algorithms and Data Structures: merge sort (National Institute of Standards and Technology)
- A new data structure for cumulative frequency tables (P. M. Fenwick, Software: Practice and Experience 24(3)) (John Wiley & Sons)
- The Java Language Specification, Java SE 25: integral types and values (Oracle)
- Fixed width integer types (cppreference.com)
- The Go Programming Language Specification: numeric types (The Go Authors)
- Number.MAX_SAFE_INTEGER (MDN Web Docs)
- bisect: array bisection algorithm (Python Software Foundation)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress