Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

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.

  • Advanced
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 7 of 35 minutes free
Free: The problem: big drops in a profit-and-loss log. With a pass: the other 14 sections and 3 interview answers, about 28 minutes.

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.

  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. [-2, -5, 1, -3] gives 4. The comparison is signed, so −2 > −10 makes days 0 and 1 a big drop.
  3. [2147483647, 1073741824] gives 0, because twice the second figure is 2,147,483,648, which a 32-bit integer cannot hold.
The log 3, 1, 4, 1, 5 with arcs for its three big drops: day 0 to day 1, day 0 to day 3, and day 2 to day 3.Big drops of [3, 1, 4, 1, 5]: earlier > 2 × later3day 01day 14day 21day 35day 43 > 23 > 24 > 23 big drops; 4 and the earlier 1 do not count

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⁵:

Every pair of days, checked Python · brute/drops_brute.py
# 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

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.

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

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.