我给出了一个数组和LR类型的查询列表,这意味着找到任意两个数组元素之间的最小绝对差值,使得它们的索引在L和R之间(包括这里)数组的起始索引是1而不是0)
例如,使用元素2 1 8 5 11获取数组a,然后查询1-3将是(2 1 8)答案将是1 = 2-1,或查询2-4(1 8 5) )答案是3 = 8-5
现在这很容易,如果你必须查看一个间隔,你对间隔进行排序,然后比较第i个元素和第i + 1个,并存储每个i的最小差异。
问题在于我需要检查很多时间间隔才能保持原始数组的完整性。
我所做的是我构建了一个新的数组b,其索引来自第一个,使得a [b [i]]&lt; = a [b [j]],i <= j。现在对于每个查询,我循环遍历整个数组,并查看b [j]是否在L和R之间,如果它将其绝对差值与第一个下一个元素进行比较,也是在L和R之间保持最小值,然后执行相同操作那个元素直到你结束。
这是低效的,因为对于每个查询,我必须检查数组的所有元素,特别是如果查询与数组的大小相比较小。我正在寻找一种节省时间的方法。
编辑:这些数字不一定是连续的,也许我给了一个糟糕的数组作为一个例子,例如,如果它是1,那我的意思是什么? 5 2然后最小的差异是1 = 2-1。在排序数组中,最小差异保证在两个连续元素之间,这就是为什么我想到排序
答案 0 :(得分:6)
我将草拟O(n (√n) log n)
- 时间解决方案,这可能足够快?当我放弃运动编程时,计算机速度要慢得多。
高层次的想法是通过以下操作将Mo的技巧应用于数据结构。
insert(x) - inserts x into the underlying multiset
delete(x) - deletes one copy of x from the underlying multiset
min-abs-diff() - returns the minimum absolute difference
between two elements of the multiset
(0 if some element has multiplicity >1)
读入所有查询间隔[l, r]
,按字典顺序非减少(floor(l / sqrt(n)), r)
对其进行排序,其中n
是输入的长度,然后处理间隔{{1在I
中插入I - I'
前一个区间的元素,删除I'
中的元素,并报告最小绝对差值。 (有趣的排序顺序的一点是,假设I' - I
查询,将操作次数从O(n^2)
减少到O(n √n)
。)
有几种方法可以实现数据结构以进行n
- 时间操作。我将使用二进制搜索树来说明清晰度,但您也可以对数组进行排序并使用分段树(如果您没有可以指定装饰的BST实现,则可以减少工作量。)
向每个BST节点添加三个字段:O(log n)
(以此节点为根的子树中的最小值),min
(以此节点为根的子树中的最大值),max
(以此节点为根的子树中值之间的最小绝对差值)。这些字段可以自下而上计算。
min-abs-diff
这个逻辑可以非常紧凑地实现。
if node v has left child u and right child w:
v.min = u.min
v.max = w.max
v.min-abs-diff = min(u.min-abs-diff, v.value - u.max,
w.min - v.value, w.min-abs-diff)
if node v has left child u and no right child:
v.min = u.min
v.max = v.value
v.min-abs-diff = min(u.min-abs-diff, v.value - u.max)
if node v has no left child and right child w:
v.min = v.value
v.max = w.max
v.min-abs-diff = min(w.min - v.value, w.min-abs-diff)
if node v has no left child and no right child:
v.min = v.value
v.max = v.value
v.min-abs-diff = ∞
if v has a left child u:
v.min = u.min
v.min-abs-diff = min(u.min-abs-diff, v.value - u.max)
else:
v.min = v.value
v.min-abs-diff = ∞
if v has a right child w:
v.max = w.max
v.min-abs-diff = min(v.min-abs-diff, w.min - v.value, w.min-abs-diff)
else:
v.max = v.value
和insert
照常工作,但装饰需要沿遍历路径更新。合理的容器选择总时间仍为delete
。
O(log n)
是通过返回min-abs-diff
来实现的,其中root.min-abs-diff
是树的根。
答案 1 :(得分:-1)
编辑#2 :我的答案决定了序列中任意两个相邻值之间的最小差异,而不是序列中任何两个值之间的最小差异。
当你说你有很多间隔要检查时,你是否意味着你必须在相同的数字序列上执行许多间隔的检查?如果是这样,如果您只是预先计算了一个数字到下一个数字的差异怎么办?例如,在Python中:
elements = [2, 1, 8, 5, 11]
def get_differences(sequence):
"""Yield absolute differences between each pair of items in the sequence"""
it = iter(sequence)
sentinel = object()
previous = next(it, sentinel)
if previous is sentinel:
return ()
for current in it:
yield abs(previous - current)
previous = current
differences = list(get_differences(elements)) # differences = [1, 7, 3, 6]
然后,当您必须找到最小差异时,只需返回min(differences[start_index:stop_index-1]
。
编辑:我错过了你的段落:
现在这很容易,如果你必须查看一个间隔,你对间隔进行排序,然后比较第i个元素和第i + 1个,并存储每个i的最小差异。
但我仍然认为我所说的是有道理的;您不必对整个集合进行排序,但仍需要执行O(n)操作。如果您在平台上处理数值,其中数字可以表示为机器整数或浮点数,那么只要您使用类似数组的容器,这应该是缓存友好且相对有效的。如果您碰巧有重复查询,您可能可以执行一些记忆来缓存预先计算的结果。