【问题标题】:What is a good data structure to represent an undirected graph?什么是表示无向图的好数据结构?
【发布时间】:2010-11-26 01:46:01
【问题描述】:

我需要构造一个无向图。我不需要它做任何太花哨的事情,但理想情况下它会像这样工作:

structure UDG = UndirectedGraph
val g = UDG.empty
val g = UDG.addEdges(g, n1, [n2, n4, n7]) (* n1 is connected to n2, n4, and n7 *)
val g = UDG.addEdge(g, n2, n3)
UDG.connected(g, n2) (* returns [n1, n3] *)

SML/NJ 中是否有良好的数据结构来模拟这些关系?我应该自己滚动吗?

更新

我已经开始尝试滚动自己的方法,但是当我尝试测试它时出现类型不匹配错误。我对 SML 结构和仿函数的经验非常基础,所以我认为我做的事情显然是错误的。我怎样才能让它工作?另外,你能帮我把它设为'a graph吗?从语义上讲,这似乎更有意义。

代码

signature ORD_NODE =
sig
  type node
  val compare : node * node -> order
  val format : node -> string
end

signature GRAPH =
sig
  structure Node : ORD_NODE
  type graph
  val empty : graph

  (* val addEdge : graph * Node.node * Node.node -> graph
  *  addEdge (g, x, y) => g with an edge added from x to y. *)
  val addEdge : graph * Node.node * Node.node -> graph

  val format : graph -> string
end

functor UndirectedGraphFn (Node : ORD_NODE) :> GRAPH =
struct
  structure Node = Node
  structure Key = struct
    type ord_key = Node.node
    val compare = Node.compare
  end
  structure Map = BinaryMapFn(Key)

  type graph = Node.node list Map.map (* Adjacency list *)
  val empty = Map.empty

  fun addEdge (g, x, y) = (* snip *)   
  fun format g = (* snip *)
end

structure UDG = UndirectedGraphFn(struct
  type node = int
  val compare = Int.compare
  val format = Int.toString
end)

错误

当我这样做时

结构 UDG = UndirectedGraphFn(结构 类型节点 = int val compare = Int.compare val 格式 = Int.toString 结尾) UDG.addEdge (UDG.empty,1,2)

我得到一个类型不匹配:

错误:运算符和操作数不一致 [字面量] 运算符域:UDG.graph * ?.UDG.node * ?.UDG.node 操作数:UDG.graph * int * int 表达: UDG.addEdge (UDG.empty,1,2)

【问题讨论】:

  • hmm 在您的错误消息中,它显示 'UDG.addEdge (UDG.empty ..... 也许它试图将字符串 'empty' 乘以 UDG.node?抱歉不熟悉 SML

标签: data-structures graph structure functor sml


【解决方案1】:

我们可以将图表示为列表的列表,我们称这种数据结构为:邻接列表 .

【讨论】:

    【解决方案2】:

    有几种可能性,具有不同的优缺点,适用于图表上的不同操作。 This nice intro 给出了使用邻接列表和邻接矩阵的背景和示例。

    以无方向的方式使用它们涉及权衡(空间与速度)。 this course material 更详细地介绍了邻接列表样式,并提供了一些关于在无向使用中使用的可能更改的想法。

    【讨论】:

    • -1 是否愿意说明原因?
    • 很抱歉,我否决了您的回答,因此它不会被自动接受。并不是说它没有帮助,只是我认为它(或其他答案)还不值得被接受。如果你继续编辑它,我会收回我的 -1。
    • 公平地说,我对您的错误消息没有太多可提供的信息,恐怕 SML 不是我的强项,尽管我会问您为什么使用链接文件...
    • 不看你的链接我觉得自己像个混蛋;我只是假设它们是标准的邻接表与矩阵的东西。尽管它们不是我想要的,但它们仍然很有帮助。
    【解决方案3】:

    好的,我不熟悉这种语言(请原谅我的无知):

    我会简单地使用以下结构:

    V.E1.E2.En+1
    V2.E1.E2.En+1
    Vn+1.E1.E2.En+1
    

    所以基本上小数点前的第一个数字代表顶点,每个边都表示后面跟着一个小数点(有点像 IP 地址)

    这样:

    可以存储为:

    1.2.5

    2.1.5.3

    3.2.4

    4.3.5.6

    5.1.2.4

    6.4

    然后在您的代码中,添加/删除边很简单,并且非常容易解析(因为顶点始终是第一个数字)

    【讨论】:

    • 这实际上是一个非常漂亮的想法。特别是如果要先填充数据结构,然后再用于可视化目的。我正在做一个爱好项目,旨在创建这样的可视化,我一定会尝试这种方法。
    【解决方案4】:

    一个非常简单的方法是哈希表,其中键作为源节点,值作为连接节点的列表。然后编写一个 add 函数,它执行两个哈希表插入,一个作为 (src, tgt),另一个作为 (tgt, src)。

    在 ocaml 中:

    let add n1 n2 =
      let aux n1 n2 =
        match Hashtbl.find_option tbl n1 with
        | None -> Hashtbl.add tbl n1 [n2]
        | Some nodes -> Hashtbl.replace tbl n1 (n2::nodes)
      in
      let _ = aux n1 n2 in
      aux n2 n1
    

    这将是一个有向图,只是您需要在插入时添加两个方向。哈希表查找函数将充当您的 connected 函数。

    (实际上,在 Ocaml 哈希表中,为一个键提供了多个值,因此您可以使用 Hashtbl.find_all 函数并保存列表。但这是最容易转换为 SML 的。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-03
      • 1970-01-01
      • 2011-11-28
      • 2015-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多