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 only 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.
Example 2:
Input: nums = [-1]
Output: [0]
Example 3:
Input: nums = [-1,-1]
Output: [0,0]
Constraints:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
Solutions
Solution 1
Thinking
Count how many later elements are smaller. A nested scan is \(O(n^2)\). The count is a prefix frequency over values already seen to the right.
Walk right to left: discretize, insert the current rank into a Fenwick tree, then query ranks strictly below it. Insert-then-query \(x-1\) still ignores equals. Reverse the answers to restore order.
The Fenwick tree is a prefix sum on the value domain. A segment tree splits that domain, supports point increment and range \([1,x-1]\), and matches Method 1 with explicit interval nodes.
The first two methods need discretization and an extra tree. During merge, when a left value is \(\le\) the current right value, exactly \(j\) consumed right elements are smaller and later; add \(j\) to that index. No value tree is required; time stays \(O(n\log n)\).
During the merge phase of merge sort, when a left element \(\textit{left}[i] \leq \textit{right}[j]\), it means exactly \(j\) elements on the right side are smaller than \(\textit{left}[i]\), so we accumulate \(j\) into the count of \(\textit{left}[i]\).
Once all right elements are exhausted, all right elements are smaller than each remaining left element, so we accumulate the right array's full length into each remaining left element's count.
Note: In C++, merge sort on very large arrays may suffer Memory Limit Exceeded. Use a buffer array to avoid excessive memory allocations.
Complexity
Time complexity: \(O(n \log n)\), the standard time complexity for merge sort.
Space complexity: \(O(n)\), the standard space complexity of recursion stack.
classSolution:defcountSmaller(self,nums:list[int])->list[int]:self.right_smaller_counts=[0]*len(nums)nums_indices=[(num,idx)foridx,numinenumerate(nums)]self.merge_sort(nums_indices)returnself.right_smaller_countsdefcombine_arrays(self,left_nums_indices:list[tuple[int,int]],right_nums_indices:list[tuple[int,int]],)->list[tuple[int,int]]:merged_nums_indices:list[tuple[int,int]]=[]left_idx,right_idx=0,0whileleft_idx<len(left_nums_indices)andright_idx<len(right_nums_indices):ifleft_nums_indices[left_idx][0]<=right_nums_indices[right_idx][0]:# Iterated left side element finalizes its right smaller count.left_num_idx=left_nums_indices[left_idx][1]self.right_smaller_counts[left_num_idx]+=right_idxmerged_nums_indices.append(left_nums_indices[left_idx])left_idx+=1continuemerged_nums_indices.append(right_nums_indices[right_idx])right_idx+=1whileleft_idx<len(left_nums_indices):# Iterated left side element finalizes its right smaller count.left_num_idx=left_nums_indices[left_idx][1]self.right_smaller_counts[left_num_idx]+=len(right_nums_indices)merged_nums_indices.append(left_nums_indices[left_idx])left_idx+=1whileright_idx<len(right_nums_indices):merged_nums_indices.append(right_nums_indices[right_idx])right_idx+=1returnmerged_nums_indicesdefmerge_sort(self,nums_indices:list[tuple[int,int]])->list[tuple[int,int]]:iflen(nums_indices)==1:returnnums_indices# Single element.split_idx=len(nums_indices)//2left_nums_indices=self.merge_sort(nums_indices[:split_idx])right_nums_indices=self.merge_sort(nums_indices[split_idx:])returnself.combine_arrays(left_nums_indices,right_nums_indices)
classSolution{private:vector<int>rightSmallerCounts;vector<pair<int,int>>buffer;voidcombineArrays(vector<pair<int,int>>&numsIndices,intleftBound,intsplitIdx,intrightBound){// Left side array = numsIndices[leftBound: splitIdx].// Right side array = numsIndices[splitIdx: rightBound + 1].intleftIdx=leftBound,rightIdx=splitIdx;intbufferIdx=leftBound;while(leftIdx<splitIdx&&rightIdx<=rightBound){if(numsIndices[leftIdx].first<=numsIndices[rightIdx].first){// Iterated left side element finalizes its right smaller count.intleftNumIdx=numsIndices[leftIdx].second;rightSmallerCounts[leftNumIdx]+=rightIdx-splitIdx;buffer[bufferIdx++]=numsIndices[leftIdx++];}elsebuffer[bufferIdx++]=numsIndices[rightIdx++];}while(leftIdx<splitIdx){// Iterated left side element finalizes its right smaller count.intleftNumIdx=numsIndices[leftIdx].second;rightSmallerCounts[leftNumIdx]+=rightIdx-splitIdx;buffer[bufferIdx++]=numsIndices[leftIdx++];}while(rightIdx<=rightBound)buffer[bufferIdx++]=numsIndices[rightIdx++];for(intidx=leftBound;idx<=rightBound;idx++)numsIndices[idx]=buffer[idx];// Put buffer data back to original array.}voidmergeSort(vector<pair<int,int>>&numsIndices,intleftBound,intrightBound){if(leftBound==rightBound)return;// Single element.// Plus 1: ensure splitIdx > leftBound.intsplitIdx=(leftBound+rightBound+1)/2;mergeSort(numsIndices,leftBound,splitIdx-1);mergeSort(numsIndices,splitIdx,rightBound);combineArrays(numsIndices,leftBound,splitIdx,rightBound);}public:vector<int>countSmaller(vector<int>&nums){buffer.resize(nums.size());// Against memory explosions.vector<pair<int,int>>numsIndices(nums.size());for(intidx=0;idx<nums.size();idx++)numsIndices[idx]={nums[idx],idx};rightSmallerCounts.assign(nums.size(),0);mergeSort(numsIndices,0,nums.size()-1);returnrightSmallerCounts;}};