找到左侧较小元素的总和
Posted
技术标签:
【中文标题】找到左侧较小元素的总和【英文标题】:finding the sum of smaller elements on left 【发布时间】:2012-03-10 13:12:19 【问题描述】:我遇到了一个问题,即在整数数组中查找每个元素左侧的较小元素的数量,这可以通过使用 二叉索引树在 O(nlgn) 中解决(如 AVL 等)或 合并排序。使用 AVL 树可以计算每个元素的左子树的大小,这将是必需的答案。但是我想不出如何有效地计算每个元素留下的较小元素的 sum。对于每个元素,我是否必须遍历左子树并对节点处的值求和,还是有更好的方法(使用合并排序等)?
例如对于数组:4,7,1,3,2
所需的答案是:0,4,0,1,1
谢谢。
【问题讨论】:
最后一个答案不应该是1
吗?另外,您能否描述一下如何使用 AVL 树解决计数问题?我不清楚。
谢谢..更正。对于计数问题,在添加任何元素时,它会与 root 进行比较,如果小于则添加到左侧,否则添加到右侧。因此,对于任何节点,其左子树的大小都会给出比它小的元素数。
是的,这样您就可以计算比它小的项目数。但它们不仅限于左侧的项目。哦,也许现在我明白了:您在插入东西后立即数较小的项目,而不是在最后,对吧?
【参考方案1】:
在二叉索引树中,您为二叉搜索树的每个节点存储子节点的数量。这使您可以找到每个节点之前的节点数(较小元素的数量)。
对于此任务,您可以为二叉搜索树的每个节点存储子节点值的总和。这使您可以找到前面节点的值的总和(较小元素的总和)。也在 O(n*log(n)) 中。
【讨论】:
【参考方案2】:检查二叉索引树上的this tutorial。这是一个结构,它使用 O(n) 内存并且可以执行这样的任务:
1. 将 a[i] 的值更改为 (to) x,调用此 add(i,x)
;
2. 返回所有a[i], iget(x).
在 O(log n)。
现在,如何将它用于您的任务。您可以分两步完成此操作。 第一步。从原始数组中复制、排序和删除重复项。现在您可以重新映射数字,使它们在 [1...n] 范围内。 步骤 2. 现在从左到右遍历阵列。设 A[i] - 原始数组中的值,new[i] - 映射值。 (如果 A = [2, 7, 11, -3, 7] 那么 new = [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]。 现在要获得新的,你可以
-
将所有元素按升序添加到映射中,例如(-3 -> 1, 2->2, 7->3, 11->4)
对于 A 中的每个元素,对 B 进行二分查找
【讨论】:
对于最后一个'7'的位置是如何确定的?是(第一个'7'的位置)-1,如果有超过2个重复,是否会类似计算? 如果你想删除重复的数组从第二个元素移动到结束,支持重复计数。如果 B[i] == B[i-cnt-1] 将 cnt 增加 1。否则如果 cnt != 0,则交换 B[i] 和 B[i-cnt]。从 B 中删除最后一个 cnt 元素。基本上,您想要获取 相对 数字,而不是初始值以使用 BIT。 对于每个节点,都有其子树中的键的总和。当合并/拆分做 sum(parent) = sum(left) + sum(right) + value。然后当得到所有的总和时,小于 X 对 X 进行切片,返回左树的总和。如果优先级是随机的,这两个操作都在 O(4 * log N) 中工作,所以你有 O(log N) 的解决方案。 @kilotaras:可能是我错过了一些非常明显的东西,但这是我所理解的......从映射中,如果我们对每个 A 的元素进行二进制搜索:2-> 2, 7->3, 11->4, -3->1 和 7-> ?我们如何为 7 再次获得“2”,以便新数组为 2,3,4,1,2 new[i] 是 A[i] 在 B 中从 1 开始的位置。您可以通过 2 种方式找到它。有一个 map:value -> 新值。对于 A 中的每个数字,对 B 进行二分搜索,得到它的位置。【参考方案3】:以下代码的复杂度为 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");
【讨论】:
以上是关于找到左侧较小元素的总和的主要内容,如果未能解决你的问题,请参考以下文章