【问题标题】:Object Oriented implementation of graph data structures图数据结构的面向对象实现
【发布时间】:2011-04-14 19:12:23
【问题描述】:

我最近一直在阅读大量图形数据结构,因为我打算编写自己的 UML 工具。据我所知,我想要的可以建模为一个由顶点和边组成的简单图。顶点将有一些值,因此最好将其表示为对象。据我所知,Edges 不需要被定向或加权,但我不想选择一个无法在以后包含此类属性的实现。

受过纯面向对象编程的教育,我首先想到的是按类表示顶点和边,例如:

Class: Vertice
    - Array arrayOfEdges;
    - String name;

Class: Edge
    - Vertice from;
    - Vertice to;

这使我有可能稍后引入权重、方向等。现在,当我阅读实现图表时,这似乎是一个非常不常见的解决方案。 Stack Overflow 上较早的问题建议使用邻接列表和邻接矩阵,但对于图来说完全是新手,我很难理解为什么这比我的方法更好。

我的应用程序最重要的方面是能够轻松计算单击和移动哪个顶点,以及添加和删除顶点和顶点之间的边的能力。这在一种实现中会比另一种更容易实现吗?

我选择的语言是 Objective-C,但我不认为这应该有任何意义。

【问题讨论】:

    标签: objective-c data-structures graph


    【解决方案1】:

    以下是两种基本图形类型及其典型实现:

    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 实现的,主要使用NSMutableDictionariesNSMutableSets(以及NSMutableArrays 用于我的有序树)。

    我的无向稀疏图只有这些 ivars,例如:

    NSMutableDictionary *vertices;
    NSMutableDictionary *edges;
    

    ivar vertices 将顶点映射到邻接映射,顶点映射到入射边 ({"vertex": {"vertex": "edge"}})
    并且 ivar edges 将边映射到事件顶点对 ({"edge": {"vertex", "vertex"}}),其中 Pair 是包含边的头顶点和尾顶点的对数据对象。

    混合稀疏图与邻接/发生列表的映射略有不同,有向稀疏图也是如此,但您应该明白这一点。

    这个实现的一个限制是,每个顶点和每个边都需要有一个与之关联的对象。为了让事情变得更有趣(sic!),每个顶点对象都必须是唯一的,每个边缘对象也是如此。这是因为字典不允许重复键。此外,对象需要实现NSCopyingNSValueTransformers 或值封装是回避这些限制的一种方法(字典键复制的内存开销也是如此)。

    虽然实施有其缺点,但有一个很大的好处:广泛的多功能性! 几乎没有任何我能想到的类型图是不可能用我已经拥有的东西来归档的。基本上,您无需使用定制部件构建每种类型的图表,而是使用您的乐高积木盒并按照您需要的方式组装图表。

    更多见解:

    每种主要的图类型都有自己的协议,这里有一些:

    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 多个图形风格,根本没有任何黑客攻击。这是值得的。

    但是,如果您只想绘制一个图形并能够在屏幕上移动其节点,那么您只需实现一个通用图形类就可以了,以后可以根据需要将其扩展到特定的方向。

    【讨论】:

    • 这简直太美了。它比我需要的要复杂得多,但是一个良好且经过良好测试的结构肯定会帮助我实现。看到 JUNG 框架是开源的,我将看看它以更好地了解它如何管理简单的图形。谢谢!
    • 顺便说一句,“jung-api”和“jung-graph-impl”是您希望在 JUNG 中查看的文件夹;)前者用于协议和装饰器魔法(文件“Graphs .java”在子文件夹“utils”中),后者用于实际的图形实现。我花了很长时间才找到荣格的核心,找到一个起点。提前知道这些应该会给你一个开端。 ;) 祝你好运!
    【解决方案2】:

    邻接矩阵在添加和删除顶点(但不是边)方面会比您的对象模型更困难,因为这涉及从矩阵中添加和删除行和列。你可以使用一些技巧来做到这一点,比如保持空行和空列,但它仍然会有点复杂。

    当在屏幕上移动一个顶点时,边缘也会被移动。这也为您的对象模型带来了一点优势,因为它会有一个连接边列表,并且不必搜索矩阵。

    两种模型都具有对边的固有定向性,因此如果您想要有无向边,那么无论哪种方式都必须做额外的工作。

    我会说总体上没有太大的区别。如果我正在实施这个,我可能会做一些类似于你正在做的事情。

    【讨论】:

      【解决方案3】:

      如果您使用的是 Objective-C,我假设您可以访问 Core Data,这可能是一个很好的起点 - 我知道您正在创建自己的图表,核心数据是,如果您正确设置架构,它可以免费进行很多您所说的检查

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-14
        • 2012-10-16
        • 1970-01-01
        • 1970-01-01
        • 2015-08-16
        • 1970-01-01
        相关资源
        最近更新 更多