【问题标题】:How to Find All Overlapping Intervals summed weight?如何找到所有重叠区间的总重量?
【发布时间】:2017-02-16 21:24:14
【问题描述】:

Possible Interview Question: How to Find All Overlapping Intervals => 为我们提供了找到所有重叠区间的解决方案。在这个问题之上,想象每个区间都有一个权重。我的目标是在插入新间隔时找到那些重叠间隔总和。

条件:新插入的区间的结束值总是大于之前插入的区间的结束点,这将导致我们已经排序结束。

当插入新区间及其权重时,应检查所有重叠区间的总权重是否超过限制。例如当我们插入[15, 70] 2时,[15, 20]的总权重将为130,它应该会报错,因为它超过了limit=128,如果没有,新插入的区间将被追加到列表中。

int limit = 128;
Inserted itervals in order:
order_come | start | end | weight
0            [10,    20]     32  
1            [15,    25]     32  
2            [5,     30]     32 
3            [30,    40]     64
4            [1,     50]     16
5            [1,     60]     16 
6            [15,    70]      2 <=should not append to the list.

Final overall summed weight view of the List after `[15, 70] 2` is inserted:
[60, 70, 2]     
[50, 60, 18]    
[40, 50, 34]    
[30, 40, 98]    
[25, 30, 66]    
[20, 25, 98]    
[15, 20, 130]  <= exceeds the limit=128, throw an error. 
[10, 15, 96]
[5, 10, 64]
[1, 5, 32]
[0, 0, 0]

感谢您宝贵的时间和帮助。

【问题讨论】:

  • O(n) 每个查询是否足够好,还是需要更好?
  • 是的,O(n) 就足够了。 @IVlad。

标签: algorithm


【解决方案1】:

O(log n) 时间的插入可以通过增强的二叉搜索树来实现。存储

order_come | start | end | weight
0            [10,    20]     32
1            [15,    25]     32
2            [5,     30]     32
3            [30,    40]     64
4            [1,     50]     16
5            [1,     60]     16

我们有一棵形状像这样的树

                   25
                 /    \
               /        \
             10          50
            /  \        /  \
           5    20    40    60
          /    /     /
         1   15    30         ,

其中每个数字表示从它到其后继的间隔。与每个树节点相关联的是两个数字。第一个我们称为 Δweight,定义为节点区间的权重减去节点父节点区间的权重,如果范围(否则为零)。第二个我们称为 Δmax,定义为与节点的后代对应的区间的最大权重减去节点的权重。

对于上面的例子,

interval | tree node | total weight | ∆weight | ∆max
[1, 5)     1           32             -32       0
[5, 10)    5           64             -32       0
[10, 15)   10          96             32        32
[15, 20)   15          128            32        0
[20, 25)   20          96             0         32
[25, 30)   25          64             64        64
[30, 40)   30          96             64        0
[40, 50)   40          32             16        64
[50, 60)   50          16             -48       80
[60, ∞)    60          0              -16       0

二叉搜索树操作几乎总是需要旋转。当我们像这样旋转一棵树时

    p          c
   / \        / \
  c   r  =>  l   p
 / \            / \
l   g          g   r

我们修改

c.∆weight += p.∆weight
g.∆weight += c.∆weight
g.∆weight -= p.∆weight
p.∆weight -= c.∆weight
p.∆max = max(0, g.∆max + g.∆weight, r.∆max + r.∆weight)
c.∆max = max(0, l.∆max + l.∆weight, p.∆max + p.∆weight).

增强的要点如下。要找到树中的最大权重,请计算 r.∆max + r.∆value,其中 r 是根。要将子树中的每个权重增加给定数量 ∂,请将子树根的 Δweight 增加 ∂。通过使用包含-排除更改 O(log n) 节点,我们可以增加整个区间。总之,这些操作允许我们在 O(log n) 时间内评估插入。

要查找区间的总权重,请正常搜索该区间,同时将该区间的祖先的 Δweight 值相加。例如,要找到 [15, 30] 的权重,我们寻找 15,遍历 25(Δweight = 64)、10(Δweight = 32)、20(Δweight = 0)和 15(Δweight = 32),总权重为 64 + 32 + 0 + 32 = 128。

为了找到假设区间内的最大总权重,我们进行了类似这样的修改搜索。使用另一个修改后的搜索,计算小于或等于start 的最大树值(predstart;如果start 是所有树值都大于start,则让predstart = -∞)并将其传递给此maxtotalweight .

maxtotalweight(root, predstart, end):
    if root is nil:
        return -∞
    if end <= root.value:
        return maxtotalweight(root.leftchild, predstart, end) + root.∆weight
    if predstart > root.value:
        return maxtotalweight(root.rightchild, predstart, end) + root.∆weight
    lmtw = maxtotalweight1a(root.leftchild, predstart)
    rmtw = maxtotalweight1b(root.rightchild, end)
    return max(lmtw, 0, rmtw) + root.∆weight

maxtotalweight1a(root, predstart):
    if root is nil:
        return -∞
    if predstart > root.value:
        return maxtotalweight1a(root.rightchild, predstart) + root.∆weight
    lmtw = maxtotalweight1a(root.leftchild, predstart)
    return max(lmtw, 0, root.rightchild.∆max + root.rightchild.∆weight) + root.∆weight

maxtotalweight1b(root, end):
    if root is nil:
        return -∞
    if end <= root.value:
        return maxtotalweight1b(root.leftchild, end) + root.∆weight
    rmtw = maxtotalweight1b(root.rightchild, end)
    return max(root.leftchild.∆max + root.leftchild.∆weight, 0, rmtw) + root.∆weight

我们假设 nil 的 Δweight = 0 和 Δmax = -∞。很抱歉遗漏了所有细节。

【讨论】:

  • 非常感谢@David Eisenstat。我有一个小问题,在插入操作期间,是否可以覆盖 O(log n) 中的所有重叠间隔总和权重?我认为您描述的算法正在执行此操作,但是我无法弄清楚我们如何为每个interval 获取total weight。例如,我们如何在 O(log n) 下获得 [15, 20) 的“total weight:128”,你能用一个小例子来解释一下,当我们尝试将[start:15, end:70] weight:2 插入到增强的 BST?我们旋转树的原因是什么?
  • @Avatar 在算法中编辑。旋转树的原因是为了在操作时间上获得最坏情况的 O(log n) 界限。
  • 要仔细检查,例如:要找到 [15,20] 的权重,我们寻找 15,遍历 25(Δweight=64)、10(Δweight=32)、20(Δ weight=0) 和 15(∆weight=32),总重量为 64+32+0+32=128,对吧?很抱歉问了太多问题,我在最初的问题中没有提到,但是当[start:45,end:55] weight:2 等新间隔到来时,您是否有任何关于我们如何 insert() 的指导?这将非常有帮助。我们是否可以在调用maxtotalweight() 之后立即执行此操作,或者我们可以在计算maxtotalweight() 时插入间隔以提高效率? @大卫艾森斯塔特
  • @Avatar 你绝对可以结合搜索来找到插入点和搜索来计算间隔最大值。这可能会有利可图,但我不确定。
  • @Avatar 插入发生在叶子上;您应该在向叶遍历时累积 Δweight,然后适当地设置新叶的 Δweight。应使用旋转逻辑中的公式为更改节点的所有祖先更新 Δmax。
【解决方案2】:

当你有时使用original answer的术语

'1E 2E 3E ... (n-1)E nE'

端点已经排序,并且您的 (n+1)st 端点比所有先前的端点更大,您只需要找到端点值大于 (n+1)st 起点的区间 (在闭合区间的情况下大于或等于)。

换句话说 - 从最右边的端点开始向左迭代间隔,直到到达端点小于或等于 (n+1)st 起点的间隔并跟踪权重总和.然后检查总和是否符合限制。最坏情况下的时间复杂度是 O(n),当所有先前的区间的终点都大于 (n+1)st 起点时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-07
    相关资源
    最近更新 更多