这是一个遵循罗马原则的实现:divide-et-impera:
首先,找到一种方法,该方法对于给定的间隔对找到重叠(如果有)。
/* Cases: A behind B, A overlap at start, A part of B, B part of A,
overlap at end, B starts after A ended:
A: 2..3..4..5
Bs: | |
0..1 | |
0..1..2..3 |
0..1..2..3..4..5..6
| 3..4 |
| 4..5..6
| | 6..7
*/
case class Interval (lower: Int, upper: Int) {
def overlap (other: Interval) : Option [Interval] = {
if (lower > other.upper || upper < other.lower) None else
Some (Interval (Math.max (lower, other.lower), Math.min (upper, other.upper)))
}
}
那是责任有限的方法,决定两个区间。
如果你不熟悉 Scala:Interval 是一个类,第一行可以理解为构造函数。Lower 和 Upper 应该是自我解释的(Int 类型)。该类有一个方法重叠,它采用该类的第二个实例(其他)并因此返回重叠的新间隔。但是包装成一个Option,意思是:如果没有发现重叠,我们返回None。如果我们找到一个,那就是 Some (Interval)。您可以帮助自己将这个构造理解为一个 List,它要么是空的,要么只包含一个元素。这是一种避免 NullpointerException 的技术,通过使用 Type 发出可能没有结果的信号。
如果一个区间的上限低于另一个区间的下限,则不可能有重叠,因此我们返回 None。
对于重叠,我们将两个下界中的最大值作为下界,将两个上界中的最小值作为新的上界。
数据:
val a = List (Interval (0, 4), Interval (7, 12))
val b = List (Interval (1, 3), Interval (5, 8), Interval (9, 11))
天真的方法:气泡重叠(首先使其工作,然后使其快速):
scala> a.map (aa => b.map (bb => aa.overlap (bb))).flatten.flatten
res21: List[Interval] = List(Interval(1,3), Interval(7,8), Interval(9,11))
如果您不习惯 Option/Maybe with Some(T) 和 None 可能有助于理解的核心是:
a.map (aa => b.map (bb => aa.overlap (bb)))
res19: List[List[Option[Interval]]] = List(List(Some(Interval(1,3)), None, None), List(None, Some(Interval(7,8)), Some(Interval(9,11))))
第一个 flatten 将两个内部 List 组合成一个 List,第二个 flatten 删除了 None 并给我们留下了 Intervals,而不是包装器 Some(Interval)。
也许我可以提出一个迭代解决方案,它的间隔时间不超过匹配的 2 倍。...(10 分钟后)... 这里是:
def findOverlaps (l1: List[Interval], l2: List[Interval]): List[Option[Interval]] = (l1, l2) match {
case (_, Nil) => Nil
case (Nil, _) => Nil
case (a :: as, b :: bs) => {
if (a.lower > b.upper) findOverlaps (l1, bs)
else if (a.upper < b.lower) findOverlaps (as, l2)
else if (a.upper > b.upper) a.overlap (b) :: findOverlaps (l1, bs)
else a.overlap (b) :: findOverlaps (as, l2)
}
}
前两行检查,如果任何一个列表是空的 - 那么就不会再有重叠了。
(a :: as, b :: bs) 是 (l1, l2) 的匹配 a 是 l1 的头部,作为 l1 的尾部(可能是 Nil),模拟 b 是 l2 的头部,bs 它的尾部。
如果 a.lower > b.upper,我们取 b 列表的尾部并使用整个 l1-list 和类似的递归重复,使用整个 l2-List 但只有 l1 列表的尾部,如果 b.lower > a.上。
否则我们应该有一个重叠,所以我们在任何一种情况下都采用 a.overlap (b) ,具有较高上限的整个列表,以及另一个列表的尾部。
scala> findOverlaps (a, b)
res0: List[Option[Interval]] = List(Some(Interval(1,3)), Some(Interval(7,8)), Some(Interval(9,11)))
你看,没有生成 None ,并且对于 findOverlaps (b, a) 相同的结果。