📅  最后修改于: 2023-12-03 15:10:21.843000             🧑  作者: Mango
给定一个整数数组,对于其中的每个元素,请统计数组中比它大的元素数目。
例如:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
一种简单的思路是,对于每个元素,遍历其右侧的元素,看是否比当前元素大,如果是就计数器+1。
但这种方法的时间复杂度为O(n^2),当数组元素很多时,计算量会非常大。
更好的方法是使用一种数据结构,可以快速的查询出某个元素右侧比它大的元素数目。常用的数据结构有二叉搜索树和树状数组。
这里介绍使用树状数组(Binary Indexed Tree)的方法。
树状数组是一种高效的动态维护数组前缀和的数据结构。它支持单点修改、区间查询,时间复杂度均为O(log n)。
我们可以用树状数组来统计一个元素右侧比它大的元素数目。
树状数组需要用一个数组bit[]
来保存前缀和,其中bit[i]
表示从[ i - 2^k + 1, i]区间的元素和,其中k是i的二进制表示中最低位的1的位数。
可视化的解释可以参考下面这张图:
我们先初始化一个树状数组,将其所有值都初始化为0。
然后,从右向左遍历数组,对于每个元素,我们先查询树状数组中比它大的元素数量,然后再将其加入到树状数组中。
树状数组增加元素的操作可以通过将数组某个位置的值加上1来实现。因为树状数组保存的是区间和,所以在增加元素时,需要将其对应的所有区间的值都加上1。
下面是代码实现:
public int[] countSmaller(int[] nums) {
int n = nums.length;
int[] ans = new int[n];
// 构建树状数组
int[] bit = new int[n + 1];
// 从右向左遍历,查询树状数组中比当前元素大的元素数量,
// 然后将当前元素加入树状数组中,并更新所有对应区间的值。
for (int i = n - 1; i >= 0; i--) {
int count = query(bit, nums[i] - 1);
ans[i] = count;
update(bit, nums[i]);
}
return ans;
}
// 查询树状数组中小于等于x的元素数量
private int query(int[] bit, int x) {
int sum = 0;
while (x > 0) {
sum += bit[x];
x -= lowbit(x);
}
return sum;
}
// 将x加入树状数组中
private void update(int[] bit, int x) {
while (x < bit.length) {
bit[x]++;
x += lowbit(x);
}
}
// 求x的二进制表示中最低位的1的位数
private int lowbit(int x) {
return x & (-x);
}
本文介绍了一种时间复杂度为O(n log n)的算法,用于统计一个数组中每个元素右侧比它大的元素数量。
树状数组是一种高效的数据结构,可以用于动态维护数组前缀和。如果您还不熟悉树状数组的使用,请务必了解一下。
这篇文章的主要思路是,从右往左遍历数组,使用树状数组来统计每个元素右侧比它大的元素数量。该方法时间复杂度为O(n log n),空间复杂度为O(n)。