【问题标题】:Data structure for non-overlapping ranges within a single dimension单个维度内不重叠范围的数据结构
【发布时间】:2008-10-17 00:21:11
【问题描述】:

我需要一个可以在单个维度内存储非重叠范围的数据结构。不需要完全覆盖维度的整个范围。

例如会议室调度程序。维度是时间。没有两个时间表可以重叠。会议室并不总是安排好的。换句话说,对于给定的时间,最多只能有一个时间表。

一个快速的解决方案是使用一个范围来存储开始和结束时间。

Range {
    Date start
    Date end
}

这是非规范化的,需要容器强制不重叠。对于两个相邻的范围,前一个的结束与下一个的开始是多余的。

另一种方案可能涉及为每个范围存储一个边界值。但是对于连续的范围序列,边界值总是比范围多一个。为了解决这个问题,序列可以表示为交替的边界值和范围:

B = 边界值,r = 范围

B-r-B-r-B

数据结构可能如下所示:

Boundary {
    Date value
    Range prev
    Range next
}

Range {
    Boundary start
    Boundary end
}

本质上它是一个具有交替类型的双向链表。

最终,我使用的任何数据结构都将在内存(应用程序代码)和关系数据库中表示。

我很好奇存在哪些学术或行业尝试过的解决方案。

【问题讨论】:

    标签: database-design data-structures normalizing


    【解决方案1】:

    表示数据的规范化方式是为每个时间单位存储一条记录。这可以在会议安排应用程序的示例中完成。您的约束将是

    的唯一约束
    (RoomId, StartTime)
    

    在连续范围的情况下,您必然需要存储 2 个东西,一个边界和第二个边界或长度。它通常是通过存储第二个边界然后在两个边界上创建一个约束来完成的

    (boundary not between colBoudaryA and colBoundaryB)
    

    带有额外的约束

    (startBoundary < endBoundary)
    

    【讨论】:

      【解决方案2】:

      双向链表效果很好,因为您只使用填充范围的内存,并且您只需要检查插入的重叠 - 在这一点上这样做几乎是微不足道的。如果有重叠,则新项目将被拒绝。

      房间号 预订编号 上一个预订ID 下一个预订ID 开始时间日期 结束时间日期 优先 用户身份

      优先级和 UserID 允许日程安排具有优先级(教授可能比学生组具有更大的影响力),以便新项目可以在插入期间“敲掉”较低优先级的项目,并且 UserID 允许要发送给遇到问题的会议组织者的电子邮件。

      您需要考虑添加一个指向每天第一次会议的表格,以便优化搜索。

      -亚当

      【讨论】:

        【解决方案3】:
        1. 对于不重叠的间隔,您可以使用起点对间隔进行排序。当您向此结构添加新间隔时,您只需检查起点和终点是否不属于此间隔集。要检查某个点 X 是否属于区间集,您可以使用二进制搜索来查找最近的起点并检查 X 是否属于它的区间。 这种方法对于修改操作不是那么理想。

        2. 您可以查看 Interval tree 结构 - 对于非重叠间隔,它具有最佳查询和修改操作。

        【讨论】:

          【解决方案4】:

          如果您足够幸运 (!) 能够使用 Postgres,您可以使用 tstzrange 列,并应用约束以防止重叠。使用范围类型的好处是它本质上会阻止开始大于结束。

          ALTER TABLE "booking" 
          ADD CONSTRAINT "overlapping_bookings" 
          EXCLUDE USING gist ("period" WITH &&, "room" WITH =);
          

          您可能需要CREATE EXTENSION IF NOT EXISTS btree_gist,因为如果没有该扩展,则不支持使用 && 创建 gist。

          【讨论】:

            【解决方案5】:

            很大程度上取决于您将如何处理数据,因此哪些操作需要高效。但是,我会考虑在 Start 和 End 的设置器中使用逻辑的 Ranges 的双向链接列表,以检查它现在是否与其邻居重叠,如果是,则缩小它们(或抛出异常,或者您想要处理尝试重叠)。

            这提供了一个很好的简单链接列表,其中包含要阅读的预定时段,但没有容器负责维护不重叠规则。

            【讨论】:

              【解决方案6】:

              这在Constraint Programming 世界中称为“一元资源”约束。这方面有很多研究,特别是针对事件时间不固定的情况,您需要为每个事件找到时间段。 有一个商业 C++ 包可以解决您的问题,还有更多 Ilog CP,但它可能有点矫枉过正。还有一个名为eclipse 的开源版本(与IDE 无关)。

              【讨论】:

                【解决方案7】:

                这很重要,因为(在数据库世界中)您必须比较多行以确定不重叠的范围。显然,当信息在内存中时,其他表示形式(例如按时间顺序排列的列表)也是可能的。不过,我认为最好使用“开始 + 结束”表示法,即使在列表中也是如此。

                有关于这个主题的整本书——“时间数据库”处理的一部分。你可以看看 Darwen、Date 和 Lorentzos “Temporal Data and the Relational Model”和(在完全不同的极端)“Developing Time-Oriented Database Applications in SQL”,Richard T. Snodgrass,Morgan Kaufmann Publishers, Inc.,旧金山,1999 年 7 月,504 +xxiii 页,ISBN 1-55860-436-7。该文件已绝版,但可在他的网站cs.arizona.edu 上以 PDF 格式提供(因此 Google 搜索很容易找到)。

                我相信其中一个相关的数据结构是R-Tree。这通常用于二维结构,但也适用于一维结构。

                您还可以查找“Allen's Relations”作为间隔 - 它们可能对您有所帮助。

                【讨论】:

                  【解决方案8】:

                  我已经成功存储了开始时间和持续时间。重叠测试类似于

                  WHERE NOT EXISTS (
                     SELECT 1 FROM table
                     WHERE BeginTime < NewBeginTime AND BeginTime + Duration > NewBeginTime
                  )
                  AND NOT EXISTS (
                     SELECT 1 FROM table
                     WHERE NewBeginTime < BeginTime AND NewBeginTime + NewDuration > BeginTime
                  )
                  

                  我认为没有测试,但希望你能理解

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-03-29
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多