↓Skip to main content
  1. LeetCode/

LeetCode LCR 170: Count Inversions (Sword Offer 51)

·3 mins· ·
LeetCode Hard Merge-Sort Divide-and-Conquer Counting
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: hard First Attempt: 2026-10-18 Source: merge sort practice ladder, step 1 (the classic problem)

Approach
#

An inversion is a pair with i < j and record[i] > record[j]. Starting from the LC 912 merge sort, only one line is added:

When the merge enters else, the right element record[j] is strictly smaller than the left element record[i] (on ties, <= takes the left first). The left half is sorted, so all of record[i..mid-1] are larger than record[j], and each forms an inversion with it: mid - i pairs.

My First Attempt
#

I adapted Krahets’ closed-interval solution to my usual half-open style:

def mergeSort(low: int, high: int):
    if high - low <= 1:
        return                                   # ① returns None

    mid = (low + high) // 2
    res = mergeSort(low, mid) + mergeSort(mid, right)   # ② right is undefined

    i, j, k = l, mid, l                          # ③ l is undefined
    while i < mid or j < high:
        if j >= high or (i < mid and record[i] <= reocrd[j]):   # ④ typo
            tmp[k] = record[i]
            i += 1
        elif i >= mid:
            tmp[k] = reocrd[j]
            j += 1
        else:
            tmp[k] = record[j]
            j += 1
            res += mid - i + 1                   # ⑤ off by one
        k += 1

    res[low: high] = tmp[low: high]              # ⑥ should write back to record
    return res

Review
#

#ProblemResult
①the base case should be return 0None + None, TypeError
② ③mixing the reference’s l, r with my low, highNameError
④reocrd typoNameError
⑤mid - i + 1logic error, no exception
⑥res is an int and cannot be slicedTypeError

⑤ is the important one. The reference uses closed intervals [l, m], where the left half has m - i + 1 elements left. With half-open [low, mid), it has mid - i. On [2, 1], my version returns 2; the correct answer is 1.

When borrowing a formula from someone else’s solution, re-derive it with your own interval convention.

The elif i >= mid branch can also go. With mid - i, an exhausted left half means i == mid, so else adds 0 and nothing is overcounted.

Solution
#

class Solution:
    def reversePairs(self, record: list[int]) -> int:
        n = len(record)
        tmp = [0] * n

        def mergeSort(low: int, high: int) -> int:
            if high - low <= 1:
                return 0

            mid = (low + high) // 2
            res = mergeSort(low, mid) + mergeSort(mid, high)

            i, j, k = low, mid, low
            while i < mid or j < high:
                if j >= high or (i < mid and record[i] <= record[j]):
                    tmp[k] = record[i]
                    i += 1
                else:
                    tmp[k] = record[j]
                    j += 1
                    res += mid - i        # every remaining left element is larger
                k += 1

            record[low:high] = tmp[low:high]
            return res

        return mergeSort(0, n)

Complexity
#

Time O(n log n), space O(n). Counting adds one O(1) addition whenever the right side is taken.

Mistakes to Avoid
#

  • Using < instead of <=: ties then take the right element first and equal pairs are counted as inversions.
  • Copying the closed-interval + 1.
  • Not scanning variable names after writing: every name used must be defined somewhere above.

Interview Explanation
#

I count during the merge step of merge sort. In a merge, every left element was originally an i and every right element a j, so each cross pair already has i < j. When a right element is smaller than the left front, all mid - i remaining left elements are larger than it, so I add them all at once. On ties I take the left first so equal pairs are not counted. Time O(n log n), space O(n).

Related

LeetCode 315: Count of Smaller Numbers After Self
·4 mins
LeetCode Hard Merge-Sort Divide-and-Conquer Binary-Indexed-Tree Counting
LeetCode 315 note: merge sort with indices, plus a BIT solution
LeetCode 327: Count of Range Sum
·3 mins
LeetCode Hard Merge-Sort Divide-and-Conquer Prefix-Sum Binary-Indexed-Tree
LeetCode 327 note: prefix sums plus merge sort counting with two pointers bounding a range, and a log of retyping it over a dozen times
LeetCode 912: Sort an Array
·3 mins
LeetCode Medium Sorting Merge-Sort Divide-and-Conquer
LeetCode 912 note: hand-written merge sort and the two bugs I hit
LeetCode 1782: Count Pairs Of Nodes
·3 mins
LeetCode Hard Two-Pointers Graph Sorting Hash-Table Counting
LeetCode 1782 note: count pairs with deg[a] + deg[b] > q using sorted degrees and converging pointers, then correct the over-count for pairs that share edges
LeetCode 2416: Sum of Prefix Scores of Strings
·2 mins
LeetCode Hard Trie String Counting
LeetCode 2416 note: each Trie node counts the words passing through it, and a word’s answer is the sum of counts along its path
LeetCode 992: Subarrays with K Different Integers
·2 mins
LeetCode Hard Sliding-Window Hash-Table Counting Array
LeetCode 992 note: “exactly k kinds” = “at most k” − “at most k - 1”, where each at-most window adds right - left + 1 per right end