以下是两种基本图形类型及其典型实现:
Dense Graphs:
Sparse Graphs:
在图形框架中(不幸的是,封闭源代码)我一直在编写(>12k loc 图实现 + >5k loc 单元测试并且仍在计数)我已经能够实现(有向/无向/混合)超图、(有向/无向/混合)多重图、(有向/无向/混合)有序图、(有向/无向/混合)KPartite图,以及各种树,例如 Generic Trees、(A,B)-Trees、KAry-Trees、Full-KAry-Trees,(即将推出的树:VP-Trees、KD-Trees、BKTrees、 B 树、R 树、八叉树、……)。
并且所有这些都没有单个顶点或边缘类。纯泛型。几乎没有多余的实现**
哦,好像这还不够,它们都以可变、不可变、可观察 (NSNotification)、线程不安全和线程安全版本的形式存在。
如何?通过过度使用Decorators.
基本上所有的图都是可变的、线程不安全的和不可观察的。所以我使用装饰器来为它们添加各种风格(导致不超过 35 个类,而如果现在没有装饰器实现则为 500 多个)。
虽然我不能给出任何实际代码,但我的图表基本上是通过Incidence Lists 实现的,主要使用NSMutableDictionaries 和NSMutableSets(以及NSMutableArrays 用于我的有序树)。
我的无向稀疏图只有这些 ivars,例如:
NSMutableDictionary *vertices;
NSMutableDictionary *edges;
ivar vertices 将顶点映射到邻接映射,顶点映射到入射边 ({"vertex": {"vertex": "edge"}})
并且 ivar edges 将边映射到事件顶点对 ({"edge": {"vertex", "vertex"}}),其中 Pair 是包含边的头顶点和尾顶点的对数据对象。
混合稀疏图与邻接/发生列表的映射略有不同,有向稀疏图也是如此,但您应该明白这一点。
这个实现的一个限制是,每个顶点和每个边都需要有一个与之关联的对象。为了让事情变得更有趣(sic!),每个顶点对象都必须是唯一的,每个边缘对象也是如此。这是因为字典不允许重复键。此外,对象需要实现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)的高度影响。
Pps:在选择这种类型的实现之前,我已经用无向图编写了它的一个小兄弟,我想扩展它以支持有向图。我的实现与您在问题中提供的实现非常相似。这就是我的第一个(相当幼稚的)项目突然结束的原因,当时:Subclassing a set of inter-dependent classes in Objective-C and ensuring type-safety 在我的图表中添加一个简单的定向会导致我的整个代码崩溃。 (我什至没有使用我当时发布的解决方案,因为它只会推迟痛苦)现在有了通用实现,我已经实现了 20 多个图形风格,根本没有任何黑客攻击。这是值得的。
但是,如果您只想绘制一个图形并能够在屏幕上移动其节点,那么您只需实现一个通用图形类就可以了,以后可以根据需要将其扩展到特定的方向。