找到间隔中具有最小绝对差异的两个元素

时间:2018-04-22 14:02:23

标签: algorithm sorting

我给出了一个数组和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。在排序数组中,最小差异保证在两个连续元素之间,这就是为什么我想到排序

2 个答案:

答案 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)操作。如果您在平台上处理数值,其中数字可以表示为机器整数或浮点数,那么只要您使用类似数组的容器,这应该是缓存友好且相对有效的。如果您碰巧有重复查询,您可能可以执行一些记忆来缓存预先计算的结果。