我正在寻求实现一个算法,该算法给出了一个整数数组和该数组中的范围(区间)列表,返回每个区间中不同元素的数量。也就是说,给定数组A和范围[i,j]返回集合{A [i],A [i + 1],...,A [j]}的大小。
显然,天真的方法(从i迭代到j并计算忽略重复)太慢了。范围和似乎不适用,因为A U B - B并不总是等于B.
我在维基百科中查找了范围查询,并暗示姚明(在'82)显示了一个算法,它为半群运算符(这种联合似乎是)提供了线性预处理时间和空间以及几乎恒定的查询时间。遗憾的是,这篇文章不是免费提供的。
显示此确切问题答案 0 :(得分:3)
有一个相当简单的算法,它使用O(N log N)时间和空间进行预处理,每个查询使用O(log N)时间。首先,创建一个持久的分段树来回答范围和查询(最初,它应该在所有位置都包含零)。然后遍历给定数组的所有元素并存储每个数字的最新位置。在每次迭代中,创建一个持久段树的新版本,将1放到每个元素的最新位置(在每次迭代时,只能更新一个元素的位置,因此段树中只有一个位置的值发生变化,因此可以在O(log N))。回答一个查询(l,r)你只需要在迭代通过初始数组的r元素时创建的树的版本找到(l,r)段的和。 希望这个算法足够快。 UPD。在我的解释中有一点错误:在每一步,段树中最多两个位置的值可能会改变(因为如果更新了数字,则必须将0放到数字的先前最新位置)。但是,它并没有改变复杂性。
答案 1 :(得分:0)
您可以通过执行二次时间预计算,在恒定时间中回答您的任何问题:
For every i from 0 to n-1
S <- new empty set backed by hashtable;
C <- 0;
For every j from i to n-1
If A[j] does not belong to S, increment C and add A[j] to S.
Stock C as the answer for the query associated to interval i..j.
此算法采用二次时间,因为对于每个区间,我们执行有限数量的操作,每个操作都需要一个恒定时间(请注意,集合S由哈希表支持),并且存在二次数的间隔。
如果您没有关于查询的其他信息(查询总数,间隔分布),则无法做得更好,因为间隔总数已经是二次方。
您可以通过n
线性动态计算来权衡二次预计算:在收到A [i..j]形式的查询后,预计算(在O(n)
时间内)回答所有时间间隔A[i..k]
,k>=i
。这将保证摊销的复杂性将保持二次,并且您不会被迫在开始时执行完整的二次预计算。
请注意,明显的算法(在语句中称之为明显的算法)是立方体,因为您完全扫描每个区间。
答案 2 :(得分:0)
这是另一种可能与分段树密切相关的方法。将数组的元素视为完整二叉树的叶子。如果数组中有2 ^ n个元素,则该完整树有n个级别。在树的每个内部节点处存储位于其下方的叶子中的点的并集。数组中的每个数字需要在每个级别中出现一次(如果有重复,则少一些)。因此,空间成本是log n的一个因素。
考虑长度为K的范围A..B。您可以通过形成与叶子和节点相关联的集合的并集,在尽可能高的树上拾取节点来计算此范围内的点的并集,只要这些节点下的子树完全包含在该范围内。如果你沿着范围挑选尽可能大的子树,你会发现子树的大小先增加然后减少,所需的子树数只会随着范围大小的对数而增长 - 在开始时如果你只能得到一个大小为2 ^ k的子树,它将在一个可被2 ^(k + 1)整除的边界上结束,你将有机会得到一个大小至少为2 ^(k + 1)的子树作为下一个如果您的范围足够大,请采取步骤。
因此,回答查询所需的半群操作数是O(log n) - 但请注意,半群操作可能很昂贵,因为您可能正在形成两个大集的并集。