题目描述
给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
样例描述
示例 1:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
示例 2:
输入:nums = [-1]
输出:[0]
思路
树状数组 (动态维护前缀和数组)
树状数组主要两个功能:①给某个位置加一个数 ②求1~x的前缀和
修改和查询的代价都是O(logn)
- 将数组的值作为至于,对应value记录这个数出现的次数。
- 从后往前遍历(因为是找右边,所以从右边开始),比某个数x小的数的个数就相当于x - 1的前缀和(value统计的都是个数),之后将x的个数加一。这样能实现动态维护前缀和数组(记录个数)
- **细节:**树状数组是从1开始的,这里数的范围是-10000到10000,所以可以将数加上10001,让范围变成1~20001。
- int[]数组转list,要么遍历一遍一个个加入到list。要么用流来包装然后转化为list。如果用
Arrays.asList()
,要用Integer数组表示
代码
class Solution {
int tree[];
int n = 20001;
public int lowbit(int x) {
return x & (-x);
}
public int query(int x) {
int ans = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
ans += tree[i];
}
return ans;
}
public void add(int x, int u) {
for (int i = x; i <= n; i += lowbit(i)) {
tree[i] += u;
}
}
public List<Integer> countSmaller(int[] nums) {
tree = new int[n + 1];
int len = nums.length;
int res[] = new int[len];
for (int i = len - 1; i >= 0; i --) {
//加上偏移10001 使得从1开始
int x = nums[i] + 10001;
res[i] = query(x - 1);
//将x的个数加一,动态维护前缀和数组
add(x, 1);
}
//int数组转化为list要包装成Integer[]
return Arrays.stream(res).boxed().collect(Collectors.toList());
}
}