我正在开发一种编程语言,我想提供一种范围数据类型,现在通常不是int
值{{1}对的列表使用(x,y)
的约束。我说的不是通常的,因为通常一个范围只是一对,但在我的情况下它超过,允许有例如
x < y
全部包含在一个对象中。
我想提供两个函数来生成联合和两个范围的检测,这两个范围应包含来自几个范围的最少数量的非重叠区间..例如
1 to 5, 7 to 11, 13 to 22
其中1 to 5 || 3 to 8 = 1 to 8
1 to 5 && 3 to 8 = 3 to 5
(1 to 3, 4 to 8) && 2 to 6 = (2 to 3, 4 to 6)
是union,||
是交集。
现在,如前所述,他们的实现只是一个列表..我知道存在一个更合适的数据结构(区间树)但是现在我更关心从其他联合/交集构建新列表列表..
哪些是实现这两个功能的最先进算法?
提前致谢
答案 0 :(得分:7)
由于您不想处理间隔树并且只使用列表,我建议您将范围表示保存为按x坐标排序的不相交间隔列表。
给定两个列表,您可以像合并排序列表一样执行一种合并步骤来计算并集和交集。
示例:
对于L1和L2的联合:
将L设为空列表。
取L1和L2中最小x的间隔并放入L.
现在再次取最小值,与L的最后一个元素进行比较,并确定它们是否需要合并(如果重叠)或者在L的末尾附加一个新的间隔(如果它们不重叠)并继续处理,就像我们合并两个排序列表。
完成后,L将成为联合,其间隔按x排序并且是不相交的。
对于交叉路口,你可以做类似的事情。
答案 1 :(得分:2)
在我看来,存储间隔的最佳方式 - 间隔树 - 也是对它们执行操作的手段。由于进行点树交叉是区间树查询支持的主要情况,因此将其扩展到区间距交叉似乎并不太难:对于tree1
中的每个区间,查询tree2
对于两个端点。对于交集,从tree1
中减去交叉间隔,对于并节,添加交叉间隔。对于每个减法/添加操作,您将获得一组新的时间间隔,以便添加到新的tree3
中,这最终将成为您操作的结果。
答案 2 :(得分:1)
没有间隔树..... 无需特殊订购...... 可能不是“最先进的”:)
(* "{ ... }" means "list" *)
function Union [{x1_,x2_},{y1_,y2_}] :=
if {x1,x2}=={} then {x1,x2}={y1,y2} (* This is needed because the intersection *)
if {y1,y2}=={} then {y1,y2}={x1,x2} (* may return an empty interval *)
(* so, empty intervals need this code *)
if {y1,y2}=={} then return[{}] (* Both intervals were empty! *)
if Min[x1,x2] > Min[y1,y2]
then
return[Union[{y1,y2},{x1,x2}]] (* or swap intervals *)
else
If Max[x1,x2] < Min[y1,y2]
then (* Non Overlapping*)
return[{{x1,x2},{y1,y2}}]
else (* Overlapping intervals *)
return[{Min[x1,x2],Max[x1,x2,y1,y2]}]
end function <Union>
function Intersection [{x1_,x2_},{y1_,y2_}] :=
if ({x1,x2}=={} OR {y1,y2}=={}) then return[{}] (* empty intervals do exist *)
if Min[x1,x2] > Min[y1,y2]
then
return[Intersection[{y1,y2},{x1,x2}]] (* or swap intervals *)
else
If Max[x1,x2] < Min[y1,y2]
then (* Non Overlapping*)
return[{}] (* HERE we create an empty interval*)
else (* Overlapping intervals *)
return[{Min[y1,y2],Min[Max[x1,x2],Max[y1,y2]]}]
end function <Intersection>
编辑&gt;
也许对n个参数的推广优于二元函数。
像&gt;之类的东西(抱歉非标准伪代码)
function GenUnion[{L:list of intervals}]:=
if (Tail[L] is interval)
return[Union[Head[L],Tail[L]]]
else
return[Union[Head[L], GenUnion[Head[Tail[L]],Tail[Tail[L]]]
end function <GenUnion>
答案 3 :(得分:0)
我将假设每个范围本身都是非重叠的,最小的,并且是有序的。
如果是这种情况,你可以“拉上他们”:
之后,如有必要,您可以通过并合并相邻的间隔。
可能有一些小的优化,但这基本上实现了联合操作,并且应该显示实现交集的一般过程。
答案 4 :(得分:0)
到目前为止我会发布我自己的实现(只是联合操作),我正在使用一种函数式语言,所以要注意......它可能会让人感到困惑:
let rec calc c l1 l2 =
match c,l1,l2 with
None, (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 < f2 -> calc (Some (f1,t1)) y1 n2
| None, n1, (f2,t2) :: y2 -> calc (Some (f2,t2)) n1 y2
| None, _, _ -> []
| (Some (fc,tc) as cur), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when t1 <= fc -> calc cur y1 n2
| (Some (fc,tc) as cur), ((f1,t1) :: y1 as n1), (f2,t2) :: y2 when t2 <= fc -> calc cur n1 y2
| Some (fc,tc), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 <= tc && t1 > fc -> calc (Some (fc,t1)) y1 n2
| Some (fc,tc), ((f1,t1) :: y1 as n1), (f2,t2) :: y2 when f2 <= tc && t2 > fc -> calc (Some (fc,t2)) n1 y2
| Some (fc,tc), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when f1 < f2 -> [fc,tc] @ calc (Some (f1,t1)) y1 n2
| Some (fc,tc), (t :: e as n1), (f2,t2) :: y2 -> [fc,tc] @ calc (Some (f2,t2)) n1 y2
| Some (fc,tc), [], (f,t) :: tr when f <= tc && t > tc -> calc (Some (fc,t)) [] tr
| Some (fc,tc), [], (f,t) :: tr when f <= tc && t <= tc -> calc (Some (fc,tc)) [] tr
| Some (fc,tc), [], x -> [fc,tc] @ x
| Some (fc,tc), (f,t) :: tr, [] when f <= tc && t > tc -> calc (Some (fc,t)) tr []
| Some (fc,tc), (f,t) :: tr, [] when f <= tc && t <= tc -> calc (Some (fc,tc)) tr []
| Some (fc,tc), x, [] -> [fc,tc] @ x
它使用c
参数存储当前间隔(合并重叠范围),而l1
和l2
是两个int*int list
。
语法很简单,每行代表一个以精确方式指定c
,l1
和l2
的案例。让我举几个例子:
(Some (fc,tc) as cur), (f1,t1) :: y1, ((f2,t2) :: y2 as n2) when t1 <= fc -> calc cur y1 n2
我有一个当前范围fc,tc
,两个列表至少包含一个元素(这就是(f1,t1)::y1
的原因),所以如果t1 <= fc
那么第一个列表的范围在当前列表之前结束并且我可以被丢弃,因此它以相同的cur
范围递归调用自己,y1
,其中第一个被丢弃,n2
是作为参数接收的相同列表的别名。
这与其他情况类似,当我发现没有下一个范围与cur
重叠时,我返回cur
作为最终答案的元素,并从空的那个开始。