hard

Count of Smaller Numbers After Self

For each element, count how many later elements are smaller than it.

1. Define the problem

Count of Smaller Numbers After Self

Given an integer array nums, return an array counts where countsi is the number of elements to the right of numsi that are smaller than numsi. A brute-force pairwise check is O(n²). Instead, pair each value with its original index and merge sort by value — whenever the merge takes a value from the left run, every right-run value already taken is both smaller and originally to the right, so add that running count directly to the left value's total.

Constraints

  • 0 ≤ nums.length ≤ 105
  • -104 ≤ numsi ≤ 104

Example

Inputnums = [5, 2, 6, 1]
Output[2, 1, 1, 0]

Explanation To the right of 5 there are 2 smaller values (2 and 1); to the right of 2 there is 1 (1); to the right of 6 there is 1 (1); to the right of 1 there are none.

2. Visualize the solution

Merge sort by value, tracking each original index

Merge sort by value, tracking each original index
Statussplit

Pair each value with its original index; split [5, 2, 6, 1] into [5, 2] and [6, 1].

What happens in this step

indexed = [{val:5,i:0}, {val:2,i:1}, {val:6,i:2}, {val:1,i:3}]
counts = [0, 0, 0, 0]

mergeSort splits at mid=2:
  left  = [{val:5,i:0}, {val:2,i:1}]
  right = [{val:6,i:2}, {val:1,i:3}]

Both halves still have 2 entries, so each recurses one level further before merging.
Step 1 of 5

Steps to visualize

  1. Pair every value with its original index so the count can be tracked as elements move during merging.
  2. Recursively split and merge like ordinary merge sort, sorting by value.
  3. Whenever a value is taken from the left run, add however many right-run values have already been taken — those are guaranteed both smaller and originally to the right.
  4. Add these counts up across every merge call at every level of the recursion.
  5. The final count for each original index is the number of smaller values that appeared after it.
3. Walk through the code

Walk through the code

Same walkthrough, now with the code. Press Next to move one step and watch which lines run.

Merge sort by value, tracking each original index
Statussplit

Pair each value with its original index; split [5, 2, 6, 1] into [5, 2] and [6, 1].

What happens in this step

indexed = [{val:5,i:0}, {val:2,i:1}, {val:6,i:2}, {val:1,i:3}]
counts = [0, 0, 0, 0]

mergeSort splits at mid=2:
  left  = [{val:5,i:0}, {val:2,i:1}]
  right = [{val:6,i:2}, {val:1,i:3}]

Both halves still have 2 entries, so each recurses one level further before merging.
Step 1 of 5
4. Solution

Solution

solution.tsTypeScript
function countSmaller(nums) {
  const n = nums.length;
  const counts = new Array(n).fill(0);
  const indexed = nums.map((val, i) => ({ val, i }));

  function mergeSort(arr) {
    if (arr.length <= 1) return arr;

    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));

    const merged = [];
    let i = 0;
    let j = 0;
    let rightTaken = 0;

    while (i < left.length && j < right.length) {
      if (left[i].val <= right[j].val) {
        counts[left[i].i] += rightTaken;
        merged.push(left[i]);
        i++;
      } else {
        rightTaken++;
        merged.push(right[j]);
        j++;
      }
    }

    while (i < left.length) {
      counts[left[i].i] += rightTaken;
      merged.push(left[i]);
      i++;
    }

    while (j < right.length) {
      merged.push(right[j]);
      j++;
    }

    return merged;
  }

  mergeSort(indexed);
  return counts;
}
Time
O(n log n)
Space
O(n)
5. Test cases

Test cases

InputExpectedCovers
nums = [5, 2, 6, 1][2, 1, 1, 0]example from the docstring
nums = [][]empty array
nums = [1][0]smallest valid input, a single element
nums = [1, 2, 3, 4][0, 0, 0, 0]already ascending, nothing smaller ever follows
nums = [4, 3, 2, 1][3, 2, 1, 0]fully descending, every later value is smaller
nums = [2, 2, 2][0, 0, 0]equal values never count as smaller
nums = [0, -1][1, 0]negative values compared against non-negative ones
nums = [1, 3, 2][0, 1, 0]only one later element is smaller than an earlier one

Keep reading

TopicDescription
Merge SortSplit the list in half, sort each half, then merge them back together in order, giving reliable O(n log n) time.
Sort ListSort a linked list in ascending order.
Merge k Sorted ListsMerge a set of sorted linked lists into a single sorted linked list.
Merge Two Sorted ArraysMerge two sorted arrays into one sorted array.
Sort an Array Using Merge SortSort an array of integers using the merge sort algorithm.
Count InversionsCount out-of-order pairs in an array using a merge-sort-based inversion count.