范围交叉/联合

时间:2010-07-08 21:23:06

标签: algorithm language-agnostic range union intersection

我正在开发一种编程语言,我想提供一种范围数据类型,现在通常不是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,||是交集。

现在,如前所述,他们的实现只是一个列表..我知道存在一个更合适的数据结构(区间树)但是现在我更关心从其他联合/交集构建新列表列表..

哪些是实现这两个功能的最先进算法?

提前致谢

5 个答案:

答案 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)

我将假设每个范围本身都是非重叠的,最小的,并且是有序的。

如果是这种情况,你可以“拉上他们”:

  1. 采取最早开始的间隔
  2. 如果其他范围的下一个间隔在此结束后开始,则输出此间隔并转到1
  3. 如果其他范围的下一个间隔在此之前结束,则丢弃该其他间隔并转到2
  4. 将另一个间隔的开头设置为等于此间隔的开头,丢弃此间隔,然后转到1
  5. 之后,如有必要,您可以通过并合并相邻的间隔。

    可能有一些小的优化,但这基本上实现了联合操作,并且应该显示实现交集的一般过程。

答案 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参数存储当前间隔(合并重叠范围),而l1l2是两个int*int list

语法很简单,每行代表一个以精确方式指定cl1l2的案例。让我举几个例子:

(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作为最终答案的元素,并从空的那个开始。