我遇到了一个问题,即在整数数组中找到每个元素左边的较小元素的数量,这可以通过使用二进制索引树在 O(nlgn)中求解(如AVL等)或合并排序。使用AVL树可以计算每个元素的左子树的大小,这将是所需的答案。但是我不能提出如何有效地计算留给每个元素的较小元素的和。对于每个元素,我是否必须遍历左子树并对节点处的值求和,还是有更好的方法(使用合并排序等)?
例如,对于数组:4,7,1,3,2
所需的ans将是:0,4,0,1,1
感谢。
答案 0 :(得分:2)
在二进制索引树中,为二叉搜索树的每个节点存储子节点数。这允许您在每个节点之前找到节点数(较小元素的数量)。
对于此任务,您可以为二叉搜索树的每个节点存储子节点值的总和。这允许您查找先前节点的值的总和(较小元素的总和)。也在O(n * log(n))。
答案 1 :(得分:2)
检查二进制索引树上的this tutorial。这是一个使用 O(n)内存并可以执行此类任务的结构:
1.将[i]的值更改为(到)x,调用此add(i,x)
;
2.返回所有a [i]的总和,i <= m,称之为get(x)
在 O(log n)。
现在,如何将此用于您的任务。您可以分两步完成。 第一步。复制,排序和删除原始数组中的重复项。现在您可以重新映射数字,因此它们在[1 ... n]范围内 步骤2.现在从左到右遍历数组。设A [i] - 是原始数组中的值,new [i] - 映射值。 (如果A = [2,7,11,-3,7]则新= [2,3,4,1,2])。
答案是get(new [i] -1)。
更新值:add(new[i], 1)
用于计数,add(new[i], A[i])
用于计算。
总而言之。排序和重新映射是 O(n logn)。处理数组是 n * O(log n) = O(n log n)。总复杂度为 O(n logn)
或者,使用treap(俄语)。
编辑:构建新阵列
假设原始数组A = [2,7,11,-3,7]
将其复制到B并排序,B = [ - 3,2,7,7,11]
做一个独特的B = [ - 3,2,7,11]
现在换新的,你可以
答案 2 :(得分:1)
以下代码的复杂度为O(nlogn)。 它使用二进制索引树来解决问题。
#include <cstdio>
using namespace std;
const int MX_RANGE = 100000, MX_SIZE = 100000;
int tree[MX_RANGE] = {0}, a[MX_SIZE];
int main() {
int n, mn = MX_RANGE, shift = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%d", &a[i]);
if(a[i] < mn) mn = a[i];
}
shift = 1-mn; // we need to remap all values to start from 1
for(int i = 0; i < n; i++) {
// Read answer
int sum = 0, idx = a[i]+shift-1;
while(idx>0) {
sum += tree[idx];
idx -= (idx&-idx);
}
printf("%d ", sum);
// Update tree
idx = a[i]+shift;
while(idx<=MX_RANGE) {
tree[idx] += a[i];
idx += (idx&-idx);
}
}
printf("\n");
}