在Java或其他命令式编程中,图表可以表示为matrix
或adjacent list
。 adjacent list
可能最受欢迎,因为它使用array
时非常简洁方便。
在函数式编程中,我们通常不使用mutable array
。那么通常我们如何在OCaml或其他函数式编程中呈现图形?
用map
?
edge-clause表格
一种方法是列出所有边,一条边是一对节点。在这种形式中,相反描绘的图表表示为以下表达式:
['h', 'g'; 'k', 'f'; 'f', 'b'; 'f', 'c'; 'c', 'b']
我们称之为edge-clause形式。显然,无法表示孤立的节点。
图表术语
另一种方法是将整个图表表示为一个数据对象。根据图形定义为一对两组(节点和边),我们可以使用以下OCaml类型:
type 'a graph_term = { nodes : 'a list; edges : ('a * 'a) list }
然后,上面的示例图表由:
表示let example_graph =
{ nodes = ['b'; 'c'; 'd'; 'f'; 'g'; 'h'; 'k'];
edges = ['h', 'g'; 'k', 'f'; 'f', 'b'; 'f', 'c'; 'c', 'b'] }
我们称之为图形术语形式。请注意,列表保持排序,它们确实是集合,没有重复的元素。每条边在边列表中只出现一次;即,从节点x到另一个节点y的边缘表示为(x,y),该对(y,x)不存在。图形术语表格是我们的默认表示。您可能希望使用集而不是列表来定义类似的类型。
邻接列表表格
第三种表示方法是将每个节点与该节点相邻的节点集相关联。我们称之为邻接表格。
以人性化的形式
到目前为止我们介绍的表示非常适合自动处理,但它们的语法不是非常用户友好。手动输入术语既麻烦又容易出错。我们可以如下定义更紧凑和“人性化”的符号:图形(带有char标记的节点)由一串原子和X-Y类型的术语表示。原子代表孤立的节点,X-Y术语代表边缘。如果X显示为边的端点,则会自动将其定义为节点。我们的例子可以写成:
"b-c f-c g-h d f-b k-f h-g"
考虑到处理效率,我认为上述4种方法都不够好。
对于record type graph-term form
,没有有效的方法来定位节点(始终为O(n)
)并找到附加到节点的边缘。
答案 0 :(得分:7)
您可能对OCamlGraph库(http://ocamlgraph.lri.fr)感兴趣,它提供了各种图形实现,包括持久性或可变性,以及一堆经典算法。
答案 1 :(得分:3)
需要考虑两件事:图表的表示,以及某些图算法中使用的中间数据结构。
我认为邻接列表表单是表示图形的最有效的内存方式。可以通过使用类型
的树或地图来改进一点type Graph a = Map a [a]
但是当你想运行像“强连接组件”或“拓扑排序”这样的算法时,那些算法很可能会使用可变数组来实现它。
请注意,在像Haskell这样的语言中,ST monad使得有可能具有临时可变状态,即数组),而不是全局状态。使用它的功能仍然是纯粹的。
答案 2 :(得分:0)
在命令式语言中,图形表示的选择取决于您尝试解决的问题。
在函数式语言中也是如此。
Ocamlgraph提供了足够的抽象,您可以编写独立于底层图表表示的算法,当然算法的运行时仍然取决于实现。