题目来源
题目描述
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
}
};
题目解析
归并排序
要计算某个元素后面比它小的个数,实际上可以统计在归并排序过程中,从它的右侧转移到它左侧的元素的个数。
- 在 merge 中,如果右半部的数组指针指向的位置为 j,左半部数组指针指向了 i
- 如果 left[i] <= right[j],则说明 right[0…j) 中所有的元素,都是从 arr[x] 的右侧转移到 arr[x] 左侧的。
举个例子,我们抽取一个「治」的子过程:
可以看到两边各自有序,且 [2,5,6,7] 一定处于原数组下标为 0-3 的位置,而 [1,3,4,9] 一定处于原数组下标为 4-7 的位置
所以 [2,5,6,7] 右边的元素就是 [1,3,4,9]
如下,1,2,3,4 已经排好序,i 处于 5 的位置,j 处于 9 的位置,i 需要向前移动一格
同时,我们可以知道右边比 5 小的元素有 3 个
总结一下:每次 i 向前移动一格,就可以知道右边比 i 位置小的元素个数
现在还有另外一个问题,在移动的过程中,每个元素的原下标早已改变,比如元素 5 原来处于下标 0 的位置,现在变到了 1 的位置
所以,我们需要定一个一个新的数据结构,记录每个元素和原下标的对应关系
也可以从后往前遍历
class Solution {
struct Node{
int val;
int idx;
Node(int v, int i) : val(v), idx(i) {
}
};
void merge(std::vector<Node *> & arr, int l, int m, int r, vector<int> & res){
std::vector<Node *> help(r - l + 1);
int i = help.size() - 1;
int p1 = m;
int p2 = r;
while (p1 >= l && p2 >= m + 1) {
// 某个元素后面比它小的个数
if(arr[p1]->val > arr[p2]->val){
res[arr[p1]->idx] += p2 - m;
}
help[i--] = arr[p1]->val > arr[p2]->val ? arr[p1--] : arr[p2--];
}
while (p1 >= l) {
help[i--] = arr[p1--];
}
while (p2 >= m + 1) {
help[i--] = arr[p2--];
}
std::copy(help.begin(), help.end(), arr.begin() + l);
}
void process(std::vector<Node *> & arr, int l, int r, vector<int> & res){
if(l == r){
return;
}
int mid = l + (r - l) / 2;
process(arr, l, mid, res);
process(arr, mid + 1, r, res);
merge(arr, l, mid, r, res);
}
public:
vector<int> countSmaller(vector<int>& nums) {
int N = nums.size();
vector<int> res(N, 0);
if(N < 2){
return res;
}
std::vector<Node *> arr(N, nullptr);
for (int i = 0; i < N; ++i) {
arr[i] = new Node(nums[i], i);
}
process(arr, 0, arr.size() - 1, res);
return res;
}
};
二分查找(待研究)
类似题目
题目 | 思路 |
---|---|
leetcode:315. 计算右侧小于当前元素的个数 Count of Smaller Numbers After Self | |
leetcode:327 区间和在指定区间的个数Count of Range Sum | |
leetcode:406. 根据身高重建队列 Queue Reconstruction by Height | |
leetcode:493. 翻转对 Reverse Pairs |