315. 计算右侧小于当前元素的个数
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
思路
归并排序
在归并排序中统计依次统计左路元素大于右路元素的个数,注意一个优化技巧,由于左右路元素已经排序完毕,在统计左路元素大于右路元素时,统计左路元素下一个时,r指针无需归位,因为左路元素已经按从小到大排序。
代码
class Solution {
public:
vector<int> countSmaller(vector<int>& nums) {
int n = nums.size();
vector<pair<int, int>> v;
vector<int> ans(n, 0);
for(int i = 0; i < n; i++) {
v.push_back(make_pair(nums[i], i));
}
mergesort(v, ans, 0, n - 1);
return ans;
}
void mergesort(vector<pair<int, int>>& v, vector<int>& ans, int left, int right) {
if(left >= right) {
return ;
}
int mid = left + (right - left) / 2;
mergesort(v, ans, left, mid);
mergesort(v, ans, mid + 1, right);
int r = mid + 1;
for(int l = left; l <= mid; l++) {
while(r <= right && v[l].first > v[r].first) {
r++;
}
ans[v[l].second] += r - mid - 1;
}
merge(v, left, mid, right);
}
void merge(vector<pair<int, int>>& v, int left, int mid, int right) {
int n = right - left + 1;
vector<pair<int, int>> tmp(n);
int l = left, r = mid + 1;
int start = 0;
while(l <= mid && r <= right) {
if(v[l].first > v[r].first) {
tmp[start++] = v[r++];
}
else {
tmp[start++] = v[l++];
}
}
while(l <= mid) {
tmp[start++] = v[l++];
}
while(r <= right) {
tmp[start++] = v[r++];
}
for(int i = 0; i < n; i++) {
v[left + i] = tmp[i];
}
}
};
树状数组
树状数组是统计前缀和一个非常有效的数据结构,假定我们从后往前遍历数组,统计数字的出现次数,那么访问到num时,只需要统计0到num-1的出现次数即可。换言之就是统计num-1的前缀次数和。
但是这种解法有一个缺点,数组的数据范围可能很大,不可能根据数组范围开数组,需要将数据进行离散化,也就是将原数据的值域映射到一个连续的整数区间,保证他们的偏序不变。这里使用原数组去重后排序,原数组每个数映射到去重排序后这个数对应位置的下标,我们称这个下标为这个对应数字的id。
代码
class Solution {
private:
vector<int> a;
vector<int> c;
void Init(int len) {
c.resize(len, 0);
}
int lowbit(int x) {
return x & (-x);
}
void update(int id) {
while(id < c.size()) {
c[id] += 1;
id += lowbit(id);
}
}
int query(int id) {
int ret = 0;
while(id > 0) {
ret += c[id];
id -= lowbit(id);
}
return ret;
}
void Discretization(vector<int>& nums) {
a.assign(nums.begin(), nums.end());
sort(a.begin(), a.end());
a.erase(unique(a.begin(), a.end()), a.end());
}
int getid(int num) {
return lower_bound(a.begin(), a.end(), num) - a.begin() + 1;
}
public:
vector<int> countSmaller(vector<int>& nums) {
int len = nums.size();
Init(len + 2);
Discretization(nums);
vector<int> ans;
for(int i = len - 1; i >= 0; i--) {
int id = getid(nums[i]);
ans.push_back(query(id - 1));
update(id);
}
reverse(ans.begin(), ans.end());
return ans;
}
};