Count of Smaller Numbers After Self
Master this topic with zero to advance depth.
Count of Smaller Numbers After Self
Counting smaller elements to the right can be solved by iterating backwards and using a data structure that supports 'insert' and 'count elements smaller than X'. Candidates include Binary Indexed Trees (BIT), Segment Trees, or modifying Merge Sort to track displacements. The Merge Sort approach is highly efficient as it naturally counts how many times an element from the right half is moved before an element from the left half.
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i].
Visual Representation
nums = [5, 2, 6, 1]
Right of 5: [2, 6, 1] -> 2 smaller (2, 1)
Right of 2: [6, 1] -> 1 smaller (1)
Right of 6: [1] -> 1 smaller (1)
Right of 1: [] -> 0 smaller
Result: [2, 1, 1, 0]Examples
To the right of 5 there are 2 smaller elements (2 and 1). To the right of 2 there is 1 smaller element (1). To the right of 6 there is 1 smaller element (1). To the right of 1 there is 0 smaller element.
Level I: Brute Force (Nested Loops)
Intuition
For each element at index i, iterate through all elements to its right (index j > i) and count how many are smaller than nums[i]. This is the most straightforward approach but inefficient for large arrays.
Detailed Dry Run
nums = [5, 2, 6, 1]
- i=0, v=5: [2, 6, 1] -> 2, 1 are smaller. Count=2.
- i=1, v=2: [6, 1] -> 1 is smaller. Count=1.
- i=2, v=6: [1] -> 1 is smaller. Count=1.
- i=3, v=1: [] -> Count=0. Result: [2, 1, 1, 0]
Level II: Better Solution (Binary Indexed Tree)
Intuition
Iterate through the array from right to left. For each element, we want to know how many elements we've already seen that are smaller than it. We can use a Binary Indexed Tree (BIT) to store the frequencies of the elements seen so far. Since the numbers can be large or negative, we use Coordinate Compression to map them to a small range [1, unique_elements].
Detailed Dry Run
BIT State Walkthrough
nums = [5, 2, 6, 1] -> Sorted Unique: [1, 2, 5, 6]
Ranks: {1:1, 2:2, 5:3, 6:4}
Backward Traversal:
1. val=1, rank=1: Query sum(0)=0. Update BIT at index 1.
2. val=6, rank=4: Query sum(3)=1. Update BIT at index 4.
3. val=2, rank=2: Query sum(1)=1. Update BIT at index 2.
4. val=5, rank=3: Query sum(2)=2. Update BIT at index 3.
Result: [2, 1, 1, 0]Level III: Optimal Solution (Modified Merge Sort)
Intuition
This approach uses the property of Merge Sort: while merging two sorted halves left and right, if we pick an element from left, any elements already moved from right to the temporary array are smaller than the current element and were originally to its right. We track original indices to update the counts array correctly.
Detailed Dry Run
Merge Step Trace
Left: [5, 2] (Indices: [0, 1])
Right: [6, 1] (Indices: [2, 3])
During Merge [5, 2] and [6, 1]:
- 1 from Right is picked: Smaller count for elements in Left increases.
- 2 from Left is picked: Counts[1] += (number of elements already picked from Right).Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.