LeetCode #315 Hard

Count of Smaller Numbers After Self

Count of Smaller Numbers After Self: for every array position, count later elements whose values are smaller.

Constraints
  • 1 <= nums.length <= 10⁵
  • -10⁴ <= nums[i] <= 10⁴
arraymerge-sortdivide-and-conquer
Open on LeetCode ↗
02

Intuition

Count of smaller numbers after self asks, for every position, how many later elements are smaller. Scanning the suffix at each index is O(n²). The way past it reuses merge sort, which already compares the two halves against each other while merging — the information is being generated and then thrown away. Here is what the merge step reveals. When both halves are sorted, every element of the right half originally sat after every element of the left half. So when a right-half value is taken before a left-half value during the merge, that right value is a smaller element appearing later — exactly what the problem counts: - Maintain a counter of right-half elements already merged; when a left element is taken, that counter is how many smaller-and-later elements it has seen at this level. Summed across all recursion levels, each left element accumulates its complete answer, because every other element is compared against it at exactly one level. One complication: merge sort moves elements, so an element's array position stops matching its original index. Pair each value with its original index before sorting, and accumulate counts into a result array indexed by that stored value. One detail decides correctness: when values are equal, take from the left half. The problem asks for strictly smaller, so an equal right-hand value must not be counted.

How to spot this pattern

A per-index question about later elements often becomes a crossing-pair count. If relationships can be counted while two sorted halves merge, indexed merge sort gives O(n log n) time.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what you would need to remember from the left to avoid re-scanning. Aim for O(n log n) time and O(n) space.

1

Pair values with original indices

Build (value, index) pairs before sorting. Merge sort moves elements, so the stored index is the only way to attribute a count back to the right position in the answer array.

2

Recurse on both halves

Split and solve each half, which counts all pairs lying entirely within one side. What remains is pairs that cross the midpoint, and those are what the merge step handles.

3

Count right-half elements as they move

During the merge, increment a counter each time a right-half pair is taken. When a left pair is taken, that counter is its answer contribution — those right elements are both smaller and originally later.

4

Take from the left on ties

When values are equal, choose the left pair and do not count it. The problem asks for strictly smaller elements, so an equal right-hand value must not contribute.

5

Accumulate across levels

Add each contribution into result[originalIndex] rather than overwriting. Every pair of elements is compared at exactly one recursion level, so the sums build to the complete answer.

6

Know the alternative structures

A Binary Indexed Tree, a segment tree, or a balanced BST over value ranks also solves count of smaller numbers after self in O(n log n), processing right to left. Merge sort is usually easier to derive from scratch; the BIT is shorter once you know it.

7

Cost of the counted merge sort

The recursion has log n levels each doing O(n) merging work, giving O(n log n) time and O(n) space for the pair array and the merge buffer.

04

Solution & live demo

▶1class Solution:
▶2 def countSmaller(self, nums:
▶3 List[int]) -> List[int]:
▶4 answer = [0] * len(nums)
▶5 pairs = [(value, i) for i, value in enumerate(nums)]
▶6 
▶7 def sort(items):
▶8 if len(items) <= 1:
▶9 return items
▶10 middle = len(items) // 2
▶11 left = sort(items[:middle])
▶12 right = sort(items[middle:])
▶13 merged = []
▶14 i = 0
▶15 j = 0
▶16 moved_right = 0
▶17 while i < len(left) and j < len(right):
▶18 if right[j][0] < left[i][0]:
▶19 merged.append(right[j])
▶20 j += 1
▶21 moved_right += 1
▶22 else:
▶23 answer[left[i][1]] += moved_right
▶24 merged.append(left[i])
▶25 i += 1
▶26 while i < len(left):
▶27 answer[left[i][1]] += moved_right
▶28 merged.append(left[i])
▶29 i += 1
▶30 merged.extend(right[j:])
▶31 return merged
▶32 
▶33 sort(pairs)
▶34 return answer
05

Common pitfalls

Counting equal values as smaller

✗ Wrong
if right[j][0] <= left[i][0]:
✓ Right
if right[j][0] < left[i][0]:

The requested relation is strictly smaller, not smaller or equal.

Losing original positions

✗ Wrong
pairs = sorted(nums)
✓ Right
pairs = [(value, i) for i, value in enumerate(nums)]

Counts must be written back in input order after values move.

Adding all right elements

✗ Wrong
answer[left[i][1]] += len(right)
✓ Right
answer[left[i][1]] += moved_right

Only right elements already moved ahead are proven smaller than the current left value.

06

Edge cases

All values are equal

Equality selects the left element, so no false smaller counts are added.

Strictly decreasing input

Each left-side item accumulates all smaller items to its right across merge levels.

Negative and duplicate values

Comparisons use actual integer values and original indices, so both are handled naturally.

07

Complexity

Time
O(n log n)
Space
O(n)
Merge sort counts each cross-half relationship while maintaining auxiliary arrays.