【问题标题】:Binary search on DateTime rangesDateTime 范围的二进制搜索
【发布时间】:2015-04-24 11:02:19
【问题描述】:

我有一个TimeRange 对象的排序列表。每个TimeRange 对象都有一个开始和结束DateTime 对象。

我有一个查询,我想返回某个范围内的TimeRanges。我目前有一个如下所示的函数

protected List<TimeRange> GetBoundedTimeRanges(List<TimeRange> timeRanges, DateTime startTime,
        DateTime endTime)
    {
        if (timeRanges == null || timeRanges.Count == 0)
        {
            return null;
        }

        var ranges = new List<TimeRange>();

        foreach (var range in timeRanges)
        {
            // If the end of the range is before the start time
            if (range.End < startTime)
            {
                continue;
            }

            // If the start of the range is after the end time
            // then break. 
            if (range.Start > endTime)
            {
                break;
            }

            // Otherwise the value falls between the range
            ranges.Add(range);
        }

        return ranges;
    }

这很慢,我想将 foreach 部分转换为二进制搜索(或任何其他合适的算法),然后使用二进制搜索从原始列表复制到新列表中但是我不确定如何去做这样做是因为我们在每个范围内都有一个开始和结束时间。任何帮助将不胜感激。

范围不重叠。例如范围 0 的结束时间总是小于范围 1 的开始时间

范围示例

Range found - Start Time 03/02/2015 22:51:50, End Time 10/03/2015 15:44:56
Range found - Start Time 10/03/2015 15:46:26, End Time 11/03/2015 08:38:56
Range found - Start Time 11/03/2015 08:43:12, End Time 13/03/2015 04:15:05
Range found - Start Time 13/03/2015 04:15:08, End Time 17/03/2015 13:38:21
Range found - Start Time 17/03/2015 13:40:00, End Time 17/03/2015 15:15:52
Range found - Start Time 17/03/2015 15:19:05, End Time 17/03/2015 15:20:42
Range found - Start Time 17/03/2015 15:39:48, End Time 24/03/2015 16:37:29
Range found - Start Time 24/03/2015 16:42:25, End Time 25/03/2015 07:46:54
Range found - Start Time 25/03/2015 07:50:23, End Time 25/03/2015 15:36:33
Range found - Start Time 25/03/2015 15:40:15, End Time 25/03/2015 15:48:44
Range found - Start Time 25/03/2015 15:52:40, End Time 25/03/2015 15:57:21
Range found - Start Time 25/03/2015 16:01:22, End Time 31/03/2015 09:18:49
Range found - Start Time 31/03/2015 09:22:12, End Time 01/04/2015 10:00:26

【问题讨论】:

  • 您的时间范围是否按顺序排列?如果按什么标准排列?如果它们是无序的,那么我不确定您是否可以做得比只查看每个并查看是否可以做的更好。如果它是有序的,那么确切的算法将取决于它的排序方式(例如,按开始日期、结束日期或更复杂的东西,如中点)。
  • 范围重叠还是不重叠?'
  • 范围不重叠。范围 0 的结束时间总是小于范围 1 的开始时间。

标签: c# binary-search


【解决方案1】:

如果您的列表按开始时间排序。您可以使用自定义比较器运行二进制搜索,以找出范围可能位于的位置。

protected List<TimeRange> GetBoundedTimeRanges(List<TimeRange> timeRanges, DateTime startTime, DateTime endTime)
{
    var startSearch = timeRanges.BinarySearch(new TimeRange(startTime, startTime), new TimeRangeComparer());
    if (startSearch < 0)
    {
        startSearch = ~startSearch;
    }

    var ranges = new List<TimeRange>();
    for (int i = startSearch; i < timeRanges.Count; i++)
    {
        var range = timeRanges[i];
        if (range.End < startTime)
        {
            continue;
        }
        if (range.Start > endTime)
        {
            break;
        }
        ranges.Add(range);
    }

    return ranges;
}

class TimeRangeComparer : IComparer<TimeRange>
{
    public int Compare(TimeRange x, TimeRange y)
    {
        var startResult = x.End.CompareTo(y.Start);
        if (startResult != 0)
        {
            return startResult;
        }

        return x.End.CompareTo(y.End);
    }
}

当我们使用 BinarySearch 时,这应该比线性算法的性能要好得多。

注意:当创建一个虚拟的TimeRange 实例进行搜索时,我使用了new TimeRange(startTime, startTime),这不是错字。这是故意的。我们不关心那里的结束时间。我们在 for 循环中过滤结束时间(您已经拥有)。

【讨论】:

  • 这更快,但是当某些时间范围介于开始时间和结束时间之间时似乎会错过。如果范围在 01/01/2014 和 10/01/2014 之间,并且我们关心 02/01/2014/ 到 04/01/2014 之间的范围,那么应该包括该范围,因为它确实包含该范围(不管事实上它还包括很多其他时间!)
  • @const_ref 我不确定我是否理解问题所在。可能存在错误(显然)。但我希望你明白这个想法,并且它没有根本性的缺陷。你能用测试数据显示失败的测试吗?
  • 嘿。使用上面的示例并传入 2015/03/10 的开始日期和 2015/03/15 的结束日期,我希望返回一个大小为 4 的列表,但是我只返回一个大小为 3 的开始日期小于 2015/03/10 即使结束日期在 2015/03/10 之后也不包含在内
  • @const_ref 很高兴你修复了它。您可以使用修复程序编辑我的答案,也可以使用最终代码编辑您的问题。这样社区就会受益。
猜你喜欢
  • 2015-08-28
  • 2015-08-27
  • 2013-03-31
  • 2021-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多