查找包含给定时间的间隔

时间:2012-12-24 09:35:09

标签: java algorithm tree intervals

我正在尝试解决以下问题:给定N个时间间隔,每个指定为(开始,结束),非重叠,基于开始排序 - 查找包含给定日期的间隔。例如:

[1,4] [5,8] [9,10][11,20]

3落入第一个间隔,15落入第四个等等。

到目前为止,我有以下基本想法:

  1. 我们可以使用二进制搜索来查找相应的间隔(Log N)
  2. 由于可能只有少数间隔很大,而其余间隔很小,因此根据持续时间对itervals进行排序可能是值得的。然后,从统计上来说,大多数情况下我们会“击中”最长的间隔(O(1)),但有时这只会导致N的最坏情况复杂。
  3. 我在想是否可以将这两种方法结合起来。另一个想法是根据持续时间进行排序,并将所有间隔插入树中,并按开始日期进行比较。在最坏的情况下,当最长持续时间按时间顺序排列时,此方法的性能等于2。

    我想象的理想解决方案是拥有一个树(或一些类似的数据结构),它将包含顶部最长的间隔,然后两个分支将具有接下来的两个最长间隔等。但是,我看不出办法在树中分支,即因为我们明确假设我们根据长度插入,我们不能真正丢弃树的左侧或右侧。

    非常感谢任何评论。

6 个答案:

答案 0 :(得分:4)

这两种方法的天真组合在大多数情况下提供O(1),并且O(logN)最坏情况,但是大O符号中的隐藏常量将是双倍的:

  1. 让原始数组为intervals
  2. 创建第二个数组,建议按降序间隔长度排序。让它为lengths
  3. 对于每个查询,使用二分搜索在intervals中进行搜索,在lengths中使用线性搜索进行搜索。搜索将并行完成 1 ,第一个完成 - 将导致另一个停止。
  4. 因为在第3步中,我们可能会在c*log(n)中执行最多intervals步骤的二分查找,所以我们最多会执行2*c*log(n)步骤。如果我们在lengths中找到更快的元素,它将导致二进制搜索在中间结束,并且我们将获得减少的操作数量(但是使用双倍常数然后是原始方法)。


    (1)不需要并行计算机,可以通过在每个计算机中搜索一步,在单个线程上模拟并行计算,直到找到答案来实现。 (一般信息,不需要理解答案:S.Even,A.Itai和A.Shamir在他们的文章On the Complexity of TimeTable and Multi Commodity Flow Problems中介绍了“并行搜索”的概念)

答案 1 :(得分:3)

您可以优先考虑二叉搜索树中的某些节点,并使其保持平衡:

从组合方法开始(将更长的时间间隔放在更靠近顶部的位置)并采用一系列旋转来使二进制搜索树平衡(同时打破某些优先级,它应该保留高优先级节点如果它们没有破坏平衡,则靠近根。)

要平衡树,请尝试此策略(未经测试):

  • 平衡左半部分(根的子树)
  • 平衡右半部分
  • 如果右半部分比左半部分高出1(AVL平衡条件
    • 虽然这是真的
      • 如果左右四分之一(右半部分的左半部分)高于右右四分之一,则向右旋转右子树(开始AVL双旋转)
      • 向左旋转树(完成AVL旋转)
      • 平衡左半边(左边的四个区域已经平衡 - 从#3开始)
  • 否则,如果左半部分高于右半部分,则超过1
    • 与另一案件相反

这应该产生一个AVL平衡树,尽可能尊重原始优先级。

答案 2 :(得分:0)

可能将两者结合在一起。这是我的想法,

根据范围的长度构建二叉树,假设我们有以下范围,

a:[0,1),b:[1,2],c:[3,10],d:[11,12],e:[13,14]

我们从底部构建树,我们根据范围大小组合叶子,所以第一轮我们可以得到内部叶子,

(a,b),c,(d,e)

然后,

(a,b,c),(d,e)

根将是,

(a,b,c,d,e)

在每一轮中,我们组合具有最小范围长度的节点,保持树的深度。

每个节点指向左,右子节点并保留子节点的最小值,最大值。

如果有任何错误,请指出..

答案 3 :(得分:0)

我会这样做:

使用第一个和最后一个间隔的数据(平均持续时间和时间),估计您希望时间在哪个区间。如果该间隔不包含目标,则使用估计的间隔重新执行估计作为一个数组结束(另一端将是最接近的实际结束)。

答案 4 :(得分:0)

我使用JodaTime和TimeSlots(几乎相当于Joda Period)实现了类似的东西。 Period是我的自定义类,具有DateTime类型的开始和结束。 我持有按结束日期排序的期间树集。

TimeSlot类:

public class TimeSlot<T> {
    private DateTime start;
    private DateTime end;

    // accessors
    ...
}

实用方法:

public TimeSlot<T> getTimeSlot(DateTime pointInTime) {
    for (TimeSlot<T> ts : getCounterTimeSlotSet()) {
        if (ts.getPeriod().isDateInRange(pointInTime)) {
            return ts;
        }
    }
    return null;
}


public boolean isDateInRange(DateTime date) {
    if (date == null) {
        return false;
    }

    return date.isAfter(this.start.minusMillis(1)) 
        && date.isBefore(this.end.plusMillis(1));
}

最近我找到了实现libraryAllen's Interval algebra,您可能会发现它很有用。

答案 5 :(得分:0)

你可能是对的,二进制搜索不是最佳的,奇数分布。虽然,N = 10亿的log_2(N)仅约为30。

如果要搜索的列表中的每个元素具有相同的可能性,则元素的二分搜索是最佳的。

因此,这里的多级二进制搜索可能是理想的,其中每个级别的间隔大致相同。使用给出的示例:

First Level: [1-10] [11-20] # Size = 10
Second Level: [1-10] = [1,4] [5,8] [9,10] # Size = 4

如果介于11和20之间,则完成,否则展开[1-10]并检查间隔[1,4] [5,8] [9,10]。

设置搜索结构需要额外花费O(N log(N)),并且对于小间隔来说成本更高,但如果存在足够大的差距,它应该具有相当好的平均搜索时间在最大和最小间隔大小之间。

在最坏的情况下,你将有log(N)级别。