以下是两种基本图形类型及其典型实现:
稠密图:
稀疏图:
在我编写的图框架中(遗憾的是不开源),我已经实现了(有向/无向/混合)超图、(有向/无向/混合)多图、(有向/无向/混合)有序图、(有向/无向/混合)K分图,以及各种树,例如通用树、(A,B)树、KAry-树、Full-KAry-树。(未来将要实现的树:VP-Trees、KD-Trees、BKTrees、B-Trees、R-Trees、Octrees等)。并且所有这些都没有单独的顶点或边缘类,完全是泛型实现。几乎没有冗余实现。
噢,如果这还不够,它们都存在着可变、不可变、可观察(NSNotification
)、线程不安全和线程安全版本。
如何实现的呢?通过大量使用装饰器模式。
基本上所有图形都是可变的、线程不安全的,也不可观察。因此,我使用装饰器来为它们添加各种风味(结果只有35个类,而不是500多个类)。
虽然我不能提供实际的代码,但我的图形基本上是通过关联表实现的,主要使用NSMutableDictionaries
和NSMutableSets
(以及我的有序树使用NSMutableArrays
)。
我的无向稀疏图只有这些实例变量,例如:
NSMutableDictionary *vertices;
NSMutableDictionary *edges;
ivar
vertices
映射顶点到相邻的顶点与关联边(
{"vertex": {"vertex": "edge"}}
)的字典,而ivar
edges
将每条边映射到其相邻的顶点对(
{"edge": {"vertex", "vertex"}}
),其中Pair是一个包含边的头部和尾部顶点的数据对象。
混合稀疏图具有稍微不同的邻接/关联列表映射,
定向稀疏图也是如此,但您应该能够理解。
这种实现的局限性在于,每个顶点和每条边都需要有与之关联的对象。为了使事情更有趣,每个顶点对象都需要是唯一的,每个边缘对象也是如此。这是因为字典不允许重复键。另外,对象需要实现
NSCopying
。然而,
NSValueTransformers
或值封装是绕过这些限制的方法(从字典键复制的内存开销也是如此)。
虽然该实现有它的缺点,但有一个巨大的好处:
极高的通用性!我几乎想不出有哪种类型的图形是无法通过我已经拥有的东西实现的。您基本上只需要从自己的乐高积木盒子里组装所需的图形,而不是使用定制构建的零件构建每种类型的图形。
每种主要的图形类型都有自己的协议,这里列举了一些:
HypergraphProtocol
MultigraphProtocol [tagging protocol] (allows parallel edges)
GraphProtocol (allows directed & undirected edges)
UndirectedGraphProtocol [tagging protocol] (allows only undirected edges)
DirectedGraphProtocol [tagging protocol] (allows only directed edges)
ForestProtocol (allows sets of disjunct trees)
TreeProtocol (allows trees)
ABTreeProtocol (allows trees of a-b children per vertex)
FullKAryTreeProtocol [tagging protocol] (allows trees of either 0 or k children per vertex)
协议的嵌套涵盖了继承(包括协议和实现)。
如果您还想了解更多信息,请随时留下评论。
附言: 为了给予应有的荣誉: 架构受到了JUNG Java图形框架(55k+ loc)的很大影响。
二话不说: 在选择这种类型的实现之前,我曾经写过一个只支持无向图的小兄弟,我希望将其扩展为也支持有向图。我的实现与你在问题中提供的实现非常相似。这就是当时导致我的第一个(相当幼稚的)项目突然结束的原因:Objective-C中子类化一组相互依赖的类并确保类型安全性将简单的有向性添加到我的图形中,使整个代码都崩溃了。(我甚至没有使用当时发布的解决方案,因为它只会推迟痛苦) 现在,我已经实现了20多种图形风格的通用实现,没有任何hack。它很值得。
如果你只想画一个图并能够在屏幕上移动其节点,那么你可以只实现一个通用的图形类,如果需要,可以稍后扩展到特定的有向性。