Graph Ocaml中无向图的循环检测

Graph Ocaml中无向图的循环检测,graph,tree,ocaml,cycle,Graph,Tree,Ocaml,Cycle,有人知道如何检测OCaml中的无向图中是否存在循环吗 下面是我用于graph的类型: type 'a graph = { nodes : 'a list; edges : ('a * 'a * int) list } 例如,我想检查这个图是否包含循环: let graph = { nodes = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'j';]; edges = [('c', 'j', 9); ('d', 'e', 8)

有人知道如何检测OCaml中的无向图中是否存在循环吗

下面是我用于graph的类型:

type 'a graph = { nodes : 'a list; edges : ('a * 'a * int) list }
例如,我想检查这个图是否包含循环:

let graph = { nodes = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'j';]; 
              edges = [('c', 'j', 9); ('d', 'e', 8); ('a', 'b', 8); ('b', 'c', 7); ('f', 'g', 6); ('b', 'h', 4); ('a', 'd', 4); ('g', 'h', 2); ('b', 'f', 2); ('e', 'g', 1)]}

在有向图和无向图中,循环的存在都是通过使用。大致上,您遍历一个图形,如果行走包含重复的节点,则存在一个循环

通常,使用附加的数据结构来标记已访问的节点。例如,我们可以使用一组数据结构(使用普通OCaml)

您还可以使用命令式哈希表而不是纯函数集。还有一种称为的算法,它可以遍历循环图而不标记所有访问的节点,这在图很大(并且无法放入内存)时非常有用


除非您这样做是为了练习,否则我建议您使用OCaml中现有的一些图形库,例如(),或()。

也可以通过从可用边列表中删除同一条边来避免访问同一条边两次;假设边之间的顺序无关紧要,可以按如下方式删除边:

let edges_remove_edge edges edge =
  let (src, dst, _) = edge in
  let rec iter edges res = match edges with
    | [] -> res
    | ((s, d, _) as e)::edges ->
       if (s = src && d = dst) then
         res @ edges
       else
         iter edges (e::res)
  in iter edges []
然后,通过构建一个与前一个图形共享数据的新图形(但具有修改的边列表),从图形中删除边:

let graph_remove_edge graph edge =
  { nodes = graph.nodes;
    edges = edges_remove_edge graph.edges edge }
然后可以沿着图遍历的递归调用变换图;本例在这里没有什么有趣的地方,只是为了演示结构:

let choose_edge graph = match graph.edges with
  | [] -> None
  | e::_ -> Some e;;

let rec visit graph = match (choose_edge graph) with
  | None -> graph
  | Some e -> visit (graph_remove_edge graph e);;

# visit graph;;
- : char graph =
{nodes = ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'j']; edges = []}
或者,使用ref跟踪当前图形:

let visit2 graph =
  let g = ref graph in
  let rec v () = match (choose_edge !g) with
  | None -> ()
  | Some e -> begin g := graph_remove_edge !g e; v () end
  in v(); !g

我使用union find数据结构设法检测循环

表示联合查找子集的结构:

let create n =
    {parent = Array.init n (fun i -> i);
     rank = Array.init n (fun i -> 0)} 
用于查找元素集的实用函数。它使用路径压缩技术:

let rec find uf i =
  let pi = uf.parent.(i) in
  if pi == i then
     i
  else begin
     let ci = find uf pi in
     uf.parent.(i) <- ci;
     ci
  end

当您在具有循环的图上使用图遍历算法时会发生什么?你做了什么实验?你在哪里卡住了?您是否有一些启动代码无法工作?如果你花多一点时间来构思你的问题,那会更有帮助。我正在实现Kruskal的最小生成树算法,我一直在检测我已经提取的边是否有一个循环。所以,我有一个像我在示例中写的那样的图,我每次都迭代它,以最小的权重取边,但我不能取一个循环的边。所以,现在我被它困住了,因为我不知道如何检测是否存在循环。如果你跟踪到目前为止看到的节点,你可以通过检查当前节点是否在看到的节点集中来检测你是否处于循环中。我无法跟踪它们,或者至少我不知道如何跟踪,因为我只考虑了边缘。所以在我看来,我每次都应该做一些算法(DST或类似的)来检查是否有一条路径通向初始节点,但我也不知道该如何做。我是OCaml的新手。我还读到Kruskal的算法应该包含联合查找算法,但我不知道如何实现它。这已经是一个更好的问题了,你应该编辑你的初始问题来添加这种最少的信息。是的,union帮助可以用来检测两个顶点是否属于图形中相同的连接组件。
let rec find uf i =
  let pi = uf.parent.(i) in
  if pi == i then
     i
  else begin
     let ci = find uf pi in
     uf.parent.(i) <- ci;
     ci
  end
let union ({ parent = p; rank = r } as uf) x y =
    let cx = find uf x in
    let cy = find uf y in
    if cx == cy then raise (Failure "Cycle detected") else  begin
       if r.(cx) > r.(cy) then
          p.(cy) <- cx
       else if r.(cx) < r.(cy) then
          p.(cx) <- cy
       else begin
          r.(cx) <- r.(cx) + 1;
          p.(cy) <- cx
       end
    end
let thereIsCycle c1 c2 g subset =
  let isCycle = try Some (union subset (findIndex c1 g.nodes) (findIndex c2 g.nodes)) with _ -> None in
      match isCycle with
     | Some isCycle -> false
     | None -> true

let rec findIndex x lst =
    match lst with
    | [] -> raise (Failure "Not Found")
    | h :: t -> if x = h then 0 else 1 + findIndex x t