找到左边较小元素的总和

时间:2012-03-10 13:12:20

标签: algorithm data-structures

我遇到了一个问题,即在整数数组中找到每个元素左边的较小元素的数量,这可以通过使用二进制索引树在 O(nlgn)中求解(如AVL等)或合并排序。使用AVL树可以计算每个元素的左子树的大小,这将是所需的答案。但是我不能提出如何有效地计算留给每个元素的较小元素的。对于每个元素,我是否必须遍历左子树并对节点处的值求和,还是有更好的方法(使用合并排序等)? 例如,对于数组:4,7,1,3,2所需的ans将是:0,4,0,1,1

感谢。

3 个答案:

答案 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] 现在换新的,你可以

  1. 添加所有元素以按递增顺序进行映射,例如(-3-> 1,2-> 2,7-> 3,11-> 4)
  2. 对于A中的每个元素,通过B
  3. 进行二进制搜索

答案 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");
}