Back to Advanced Data Structures🏛️ Visual Logic: BIT Processing
Count of Smaller Numbers After Self
Count of Smaller Numbers After Self
🧩 Problem Statement
Given an integer array nums, return an integer array counts where counts[i] is the number of smaller elements to the right of nums[i].
🔹 Example 1:
Input:
nums = [5,2,6,1]
Output:
[2,1,1,0]
Explanation:
- 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 elements.
🔹 Example 2:
Input:
nums = [-1]
Output:
[0]
🔍 Approaches
1. Binary Indexed Tree (Fenwick Tree) - Optimal
- Concept: Iterate through the array from right to left.
- Ideally, we want to know: "How many numbers currently 'visited' are smaller than
current_num?" - We can track frequencies of visited numbers in a BIT.
- Query:
sum(0 to current_num - 1)gives counts of smaller visited numbers. - Update:
add(current_num, 1)marks the current number as visited. - Coordinate Compression: Since
nums[i]can be large or negative (e.g.,-10^4to10^4), we map them to ranks1toNbased on sorted order.
2. Merge Sort (Alternative)
- During the "merge" step, if we pick a number from the right subarray (smaller), we know it contributes to the "inversion count" of remaining elements in the left subarray.
🏛️ Visual Logic: BIT Processing [5, 2, 6, 1]
Ranks: 1->1, 2->2, 5->3, 6->4 (Sorted: 1, 2, 5, 6).
Array mapped to ranks: [3, 2, 4, 1].
Step 1: Process Index 3 (Value 1, Rank 1)
- BIT State:
[0, 0, 0, 0, 0](Empty). - Query(Rank 1 - 1):
query(0). Result =0. - Update(Rank 1): Add 1 at idx 1.
- Counts:
[?, ?, ?, 0].
Step 2: Process Index 2 (Value 6, Rank 4)
- BIT State: Has
1at idx 1. - Query(Rank 4 - 1):
query(3). Sum of freq in[1..3]. Result =1(from Rank 1). - Update(Rank 4): Add 1 at idx 4.
- Counts:
[?, ?, 1, 0].
Step 3: Process Index 1 (Value 2, Rank 2)
- BIT State: Has
1at idx 1,1at idx 4. - Query(Rank 2 - 1):
query(1). Result =1(Rank 1). - Update(Rank 2): Add 1 at idx 2.
- Counts:
[?, 1, 1, 0].
Step 4: Process Index 0 (Value 5, Rank 3)
- BIT State: Has
1at idx 1, 2, 4. - Query(Rank 3 - 1):
query(2). Sum[1..2]. Result =2(Rank 1 and 2). - Update(Rank 3): Add 1 at idx 3.
- Counts:
[2, 1, 1, 0].
⏳ Time & Space Complexity
- Time Complexity: $O(N \log N)$ (Coordinate Compression + N queries on BIT).
- Space Complexity: $O(N)$ for BIT and Rank Map.
🚀 Code Implementations
C++
#include <vector>
#include <algorithm>
#include <map>
using namespace std;
class Solution {
vector<int> bit;
int n;
void update(int idx, int val) {
for (; idx <= n; idx += idx & -idx)
bit[idx] += val;
}
int query(int idx) {
int sum = 0;
for (; idx > 0; idx -= idx & -idx)
sum += bit[idx];
return sum;
}
public:
vector<int> countSmaller(vector<int>& nums) {
int sz = nums.size();
vector<int> sortedN = nums;
sort(sortedN.begin(), sortedN.end());
// Coordinate Compression: Value -> Rank (1-based)
// Use unique to handle duplicates if strictly smaller logic?
// Problem asks strict smaller. Equal doesn't count.
// lower_bound gives first occurrence, effectively handling duplicates correctly for rank mapping?
// Actually we need `lower_bound` index + 1.
n = sz; // BIT size limit
bit.assign(n + 1, 0);
vector<int> res(sz);
for (int i = sz - 1; i >= 0; --i) {
// Rank: index in sorted array + 1
int rank = lower_bound(sortedN.begin(), sortedN.end(), nums[i]) - sortedN.begin() + 1;
// Query: sum of items with rank < current_rank
res[i] = query(rank - 1);
// Update: add 1 to current_rank
update(rank, 1);
}
return res;
}
};
Python
from typing import List
import bisect
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
# Coordinate Compression
sorted_nums = sorted(nums)
ranks = {val: i + 1 for i, val in enumerate(sorted_nums)}
# Wait, simple map fails duplicates?
# If duplicates exist, standard 'rank' usually implies unique values?
# Actually simplest is just bisect_left for on-the-fly rank, or mapping unique values.
# Let's map unique sorted values.
unique_sorted = sorted(list(set(nums)))
rank_map = {val: i + 1 for i, val in enumerate(unique_sorted)}
n = len(unique_sorted)
bit = [0] * (n + 1)
def update(i, delta):
while i <= n:
bit[i] += delta
i += i & (-i)
def query(i):
s = 0
while i > 0:
s += bit[i]
i -= i & (-i)
return s
res = []
for x in reversed(nums):
rank = rank_map[x]
res.append(query(rank - 1))
update(rank, 1)
return res[::-1]
Java
import java.util.*;
class Solution {
int[] bit;
int n;
private void update(int idx, int val) {
while (idx <= n) {
bit[idx] += val;
idx += idx & -idx;
}
}
private int query(int idx) {
int sum = 0;
while (idx > 0) {
sum += bit[idx];
idx -= idx & -idx;
}
return sum;
}
public List<Integer> countSmaller(int[] nums) {
int[] sorted = nums.clone();
Arrays.sort(sorted);
// Remove duplicates for cleaner ranking?
// Or simply iterate?
// With duplicates: {1, 1}. Rank 1 and 2? No, same rank makes sense or relying on binary search.
// Using unique ranks is standard.
// Let's create a map of unique ranks
Map<Integer, Integer> ranks = new HashMap<>();
int rank = 1;
for (int num : sorted) {
if (!ranks.containsKey(num)) {
ranks.put(num, rank++);
}
}
n = ranks.size();
bit = new int[n + 1];
List<Integer> res = new ArrayList<>();
for (int i = nums.length - 1; i >= 0; i--) {
int r = ranks.get(nums[i]);
res.add(query(r - 1));
update(r, 1);
}
Collections.reverse(res);
return res;
}
}
🌍 Real-World Analogy
Game Leaderboard Ranking:
Imagine players joining a game server with different scores. You want to tell each player: "You are better than X players who joined AFTER you." (Wait, problem is Reverse: Right side).
Actually, simpler: Traffic Overtaking.
You are driving on a highway. You ($V_i$) look ahead (Right side of array). How many cars ahead of you satisfy ($V_{ahead} < V_i$) i.e., are driving slower than you? These are the cars you will inevitably overtake.
- BIT helps keep a live frequency count of speeds observed so far (scanning backwards from horizon to you).
Solution Code
O(n) TimeO(1) Space
#include <algorithm>
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
class Solution {
vector<int> bit;
int n;
void update(int idx, int val) {
for (; idx <= n; idx += idx & -idx)
bit[idx] += val;
}
int query(int idx) {
int sum = 0;
for (; idx > 0; idx -= idx & -idx)
sum += bit[idx];
return sum;
}
public:
vector<int> countSmaller(vector<int> &nums) {
int sz = nums.size();
vector<int> sortedN = nums;
sort(sortedN.begin(), sortedN.end());
// Unique for correct ranking map
sortedN.erase(unique(sortedN.begin(), sortedN.end()), sortedN.end());
n = sortedN.size();
bit.assign(n + 1, 0);
vector<int> res(sz);
for (int i = sz - 1; i >= 0; --i) {
// Rank: index in sorted unique array + 1
int rank = lower_bound(sortedN.begin(), sortedN.end(), nums[i]) -
sortedN.begin() + 1;
res[i] = query(rank - 1);
update(rank, 1);
}
return res;
}
};
int main() {
Solution sol;
// Test 1: [5,2,6,1] -> [2,1,1,0]
vector<int> nums1 = {5, 2, 6, 1};
vector<int> expected1 = {2, 1, 1, 0};
assert(sol.countSmaller(nums1) == expected1);
cout << "Test 1 Passed" << endl;
// Test 2: [-1] -> [0]
vector<int> nums2 = {-1};
vector<int> expected2 = {0};
assert(sol.countSmaller(nums2) == expected2);
cout << "Test 2 Passed" << endl;
// Test 3: [-1, -1] -> [0, 0]
vector<int> nums3 = {-1, -1};
vector<int> expected3 = {0, 0};
assert(sol.countSmaller(nums3) == expected3);
cout << "Test 3 Passed" << endl;
return 0;
}
SYSTEM STABLEUTF-8 • STATIC_RENDER