给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
分析:
这个题实际上就是求解各个逆序对,而如何求解逆序对,已经遇到过:数组中的逆序对,采用的是分治的方法。与这道题不同的是,本题需要分别求出各数的逆序对。
为了解决这个问题,可以采用“索引数组”的方法。如果元素在算法的执行过程中位置发生变化,而我们还想要定位它原来的位置,那么可以使用索引数组。技巧在于用原始数组的元素来比较大小,而不改变原始数组的顺序,真正发生位置变化的是索引数组中的相对顺序。这样我们就可以利用索引来定位了。
class Solution {
public:
void merge(int left, int mid, int right, vector<int> &nums, vector<int> &index, vector<int> &temp, vector<int> &res){
int i = left, j = mid + 1, k = left;
while(i <= mid && j <= right){
if(nums[index[i]] <= nums[index[j]]){
temp[k++] = index[i];
res[index[i]] += j - mid - 1;
i++;
}else{
temp[k++] = index[j++];
}
}
while(i <= mid){
temp[k++] = index[i];
res[index[i]] += j - mid - 1;
i++;
}
while(j <= right){
temp[k++] = index[j++];
}
for(int i = left; i <= right; i++) index[i] = temp[i];
}
void mergeSort(int left, int right, vector<int> &nums, vector<int> &index, vector<int> &temp, vector<int> &res){
if(left < right){
int mid = (left + right) / 2;
mergeSort(left, mid, nums, index, temp, res);
mergeSort(mid + 1, right, nums, index, temp, res);
merge(left, mid, right, nums, index, temp, res);
}
}
vector<int> countSmaller(vector<int>& nums) {
int n = nums.size();
if(n == 0) return {};
vector<int> index(n);
vector<int> temp(n);
vector<int> res(n, 0);
for(int i = 0; i < n; i++) index[i] = i;
mergeSort(0, n - 1, nums, index, temp, res);
return res;
}
};