我最近一直在阅读相当多的图形数据结构,因为我打算编写自己的UML工具。据我所见,我想要的可以被建模为由顶点和边组成的简单图形。顶点将具有一些值,并且最好表示为对象。据我所知,边缘不需要既不是定向的也不是加权的,但我不想选择一种以后不可能包含这些属性的实现。
受过纯面向对象编程的教育,我想到的第一件事是按类表示顶点和边,例如:
Class: Vertice
- Array arrayOfEdges;
- String name;
Class: Edge
- Vertice from;
- Vertice to;
这使我有可能在以后引入权重,方向等。现在,当我阅读实现图表时,似乎这是一个非常罕见的解决方案。 Stack Overflow上的早期问题提出了邻接列表和邻接矩阵,但对图表来说是全新的,我很难理解为什么这比我的方法更好。
我的应用程序最重要的方面是能够轻松计算单击和移动的顶点,以及在顶点之间添加和删除顶点和边的功能。这在一个实现中比另一个更容易实现吗?
我选择的语言是Objective-C,但我认为这不应该具有任何意义。
答案 0 :(得分:13)
以下是两种基本图形类型及其典型实现:
<强> Dense Graphs 强>
<强> Sparse Graphs 强>
在图表框架(不幸的是,闭源,我写的)(&gt; 12k loc图表实现+&gt; 5k loc单元测试并仍在计算中)我已经能够实现(定向/无向/混合)超图,(有向/无向/混合)多图,(有向/无向/混合)有序图,(有向/无向/混合)KPartite图,以及所有各种树木,如通用树木,(A,B) - 树木,KAry树木,全KAry树木,(未来的树木: VP树木,KD树木, BKTrees,B-Trees,R-Trees,八分之一,......)
并且没有单个顶点或边缘类。纯粹的仿制药。几乎没有多余的实施**
哦,好像这还不够,它们都是可变的,不可变的,可观察的(NSNotification
),线程不安全和线程安全的版本。
怎么样?过度使用Decorators
基本上所有图形都是可变的,线程不安全且不可观察。因此,我使用装饰器为它们添加各种风格(导致不超过35个类,如果没有装饰器,则立即实现500 +)。
虽然我无法提供任何实际代码,但我的图表基本上是通过Incidence Lists实现的,主要使用NSMutableDictionaries
和NSMutableSets
(以及NSMutableArrays
用于我的有序树)。
我的 Undirected Sparse Graph 除了这些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)
协议嵌套意味着吸收(两种协议,以及实现)。
如果还有其他任何想要获得一些观点的话,请随时发表评论。
Ps :在信用到期时给予信任:建筑受到了很大的影响 JUNG Java图表框架(55k + loc)。
Pps :在选择这种类型的实现之前,我曾用无向图编写了一个小兄弟,我想扩展为支持有向图。我的实现与您在问题中提供的实现非常相似。这就是我的第一个(相当天真)项目突然结束的原因:Subclassing a set of inter-dependent classes in Objective-C and ensuring type-safety在我的图表中添加一个简单的定向导致我的整个代码分崩离析。 (我甚至没有使用我当时发布的解决方案,因为它只会推迟痛苦)现在通过通用实现,我实现了超过20种图形风格,完全没有黑客攻击。这是值得的。
如果您只想绘制一个图形并且能够在屏幕上移动它的节点,那么只需实现一个通用图形类就可以了,如果需要的话可以在以后扩展到特定的直接性。
答案 1 :(得分:1)
查看此powerpoint演示文稿,尤其是最后一张幻灯片:http://digital.cs.usu.edu/~cyan/CS5050/Graph.ppt
答案 2 :(得分:1)
在添加和删除顶点(但不包括边)时,邻接矩阵比对象模型更难一些,因为这涉及在矩阵中添加和删除行和列。有一些技巧可以用来做这个,比如保持空行和列,但它仍然有点复杂。
在屏幕周围移动顶点时,边缘也会移动。这也为您的对象模型提供了一些优势,因为它将具有连接边的列表,而不必搜索矩阵。
两种模型都具有对边缘的固有指向性,因此如果您想要具有无向边,那么您将不得不以任何方式进行额外的工作。
我想说总的来说并没有太大的区别。如果我实现这一点,我可能会做一些类似于你正在做的事情。
答案 3 :(得分:0)
如果您使用的是Objective-C,我假设您可以访问核心数据,这可能是一个很好的起点 - 我了解到您正在创建自己的图形,强度为核心数据是,如果您正确设置架构,它可以免费进行大量的检查