【问题标题】:Java: how to represent graphs?Java:如何表示图形?
【发布时间】:2010-12-16 19:05:06
【问题描述】:

我正在实施一些算法来自学图表以及如何使用它们。你会推荐什么是在 Java 中做到这一点的最佳方法?我在想这样的事情:

public class Vertex {

    private ArrayList<Vertex> outnodes; //Adjacency list. if I wanted to support edge weight, this would be a hash map.

    //methods to manipulate outnodes
}

public class Graph {
    private ArrayList<Vertex> nodes;
    //algorithms on graphs
}

但我基本上只是编造了这个。有没有更好的办法?

另外,我希望它能够支持普通图的变体,例如有向图、加权边、多重图等。

【问题讨论】:

  • 你是从什么开始的?我正在为考试做准备,我还有一天的时间。我将不得不为少量节点编写一个程序。我也和你想的一样。

标签: java graph


【解决方案1】:

每个节点都是唯一命名的,并且知道它连接到谁。连接列表允许一个节点连接到任意数量的其他节点。

public class Node {
    public String name;
    public List<Edge> connections;
}

每个连接都是有向的,有起点和终点,并且是加权的。

public class Edge {
    public Node start;
    public Node end;
    public double weight;
}

图表只是您的节点集合。而不是List&lt;Node&gt;,请考虑Map&lt;String, Node&gt;,以便按名称快速查找。

public class Graph {
    List<Node> nodes;
}

【讨论】:

  • 也许最好将连接命名为邻居。此外,如果我们不单独使用 Edge,我们已经知道它的起点,因此可以在大多数用例中将其移除以提高空间效率
  • 我可以想象一个不包括边的图。如果您不需要边缘上的权重,或者您不需要方向,这可能是一个很好的简化。
  • 我的意思是我们应该将边作为一个列表包含在内,这对于大多数问题来说是一个很好的策略,但我们可能不需要边对象中的“节点开始”对象,因为在遍历图的节点时,我们知道遍历的节点是起点。顺便说一句,连接可以更改为“边缘”,我的建议邻居是一个更糟糕的名字:(
【解决方案2】:

如果您需要加权边和多重图,您可能需要添加另一个类 Edge

我还建议使用 generics 来指定当前使用的 Vertex 和 Edge 的子类。例如:

public class Graph<V extends Vertex> {
List<V> vertices;
...
}

在实现图算法时,您还可以为您的图类定义接口,算法可以在这些类上运行,这样您就可以尝试实际图表示的不同实现。例如,连接良好的简单图可能由 adjacency matrix 更好地实现,稀疏图可能由 adjacency lists 表示 - 这一切都取决于...

顺便说一句,有效地建造这样的结构可能非常具有挑战性,所以也许您可以给我们提供更多详细信息,说明您希望将它们用于什么样的工作?对于更复杂的任务,我建议您查看各种 Java 图形库,以获得一些灵感。

【讨论】:

  • 我真的没有任何细节......我的自学目标是非常开放的。我只是想设置一些图,找到一些最小生成树,对 graphviz 做一些输出,计算拓扑排序等。
  • 您能说明一下如何使用泛型吗?
  • 啊,感谢您的澄清。如果它只是为了玩,我认为邻接列表几乎可以完成这项工作......它似乎不是性能密集型的。希望我澄清了泛型部分。
【解决方案3】:

看看http://jung.sourceforge.net/doc/index.html 图形库。您仍然可以练习实现自己的算法(可能是广度优先或深度优先搜索开始),但您不必担心创建图形结构。

【讨论】:

  • bfs 和 dfs 在图中?
【解决方案4】:

为什么不让事情变得简单并使用adjacency matrixadjacency list

【讨论】:

    【解决方案5】:

    前段时间我遇到了同样的问题并做了我自己的实现。我建议你实现另一个类:Edge。那么,一个顶点就会有一个边列表。

    public class Edge {
        private Node a, b;
        private directionEnum direction;     // AB, BA or both
        private int weight;
        ...
    }
    

    它对我有用。但也许就是这么简单。如果您查看它的代码,这个库可能会对您有所帮助:http://jgrapht.sourceforge.net/

    【讨论】:

      【解决方案6】:

      当您到达要渲染图表的地步时,我强烈推荐graphviz

      及其伙伴:看看Laszlo Szathmary's GraphViz class,以及notugly.xls

      【讨论】:

      【解决方案7】:

      即使在提出这个问题的时候,也就是 3 年前,Sage(完全免费)就已经存在并且非常擅长图论。但是,在 2012 年,它是最好的图论工具。因此,Sage 已经内置了大量的图论材料,包括其他免费和开源的东西。因此,无需编程即可轻松搞定各种事情以了解更多信息。

      而且,如果您也对编程部分感兴趣,首先 Sage 是开源的,因此您可以查看任何已经存在的代码。其次,如果你真的想练习,你可以重新编程你想要的任何功能,或者你可以第一个编程一些不存在的东西。在后一种情况下,您甚至可以提交新功能并使 Sage 对所有其他用户更好。

      目前,这个答案可能对 OP 没有那么有用(因为它已经 3 年了),但希望它对将来看到这个问题的任何其他人有用。

      【讨论】:

      • 似乎是 Python。这个问题清楚地询问了 Java。
      【解决方案8】:

      Graph 的邻接表实现适用于解决大多数与图相关的问题。

      在我的博客上,相同的 Java 实现是 here

      【讨论】:

        【解决方案9】:
        class Graph<E> {
            private List<Vertex<E>> vertices;
        
            private static class Vertex<E> {
                E elem;
                List<Vertex<E>> neighbors;
            }
        }
        

        【讨论】:

          【解决方案10】:

          'Robert Sedgwick' 和'Kevin Wayne' 编写的简单表示可在http://algs4.cs.princeton.edu/41graph/Graph.java.html 获得

          从上一页复制的解释。

          Graph 类表示顶点的无向图 命名为 0 到 V - 1.

          它支持以下两个主要操作:向图中添加一条边, 遍历与顶点相邻的所有顶点。它还提供 返回顶点数V和数量的方法 E 的边数。允许平行边和自环。 按照惯例,一个自循环 v-v 出现在 v 的邻接列表两次并且对度数贡献了两次 v.

          此实现使用邻接列表表示,它 是 Bag 对象的顶点索引数组。 所有操作都需要恒定的时间(在最坏的情况下),除了 迭代与给定顶点相邻的顶点,这需要 时间与此类顶点的数量成正比。

          【讨论】:

            【解决方案11】:

            学习算法时,在决定表示时不应考虑编程语言 (Java)。每个问题都可以从独特的表示中受益,而且设计它可以增加一些学习。先不依赖特定语言解决问题,然后任何特定语言的表示都会自然流动。

            当然,一般表示和库在实际应用中很有用。但其中一些也可以从一些定制中受益。使用其他答案来了解可用的不同技术,但在适当的时候考虑定制。

            【讨论】:

              【解决方案12】:
              class Vertex {
                  private String name;
                  private int score; // for path algos
                  private boolean visited; // for path algos
                  List<Edge> connections;
              }
              
              class Edge {
                  private String vertex1Name; // same as Vertex.name
                  private String vertex2Name;
                  private int length;
              }
              
              class Graph {
                  private List<Edge> edges;
              }
              

              【讨论】:

              • 你的边只知道顶点的名字。因此,您无法使用您显示的结构遍历图表。
              • 通过从节点名称重新创建节点的附加逻辑,我们仍然可以进行遍历。我这样做是为了节省空间,这样我们就不必在 Edge 中再次保存完整的顶点。这就像只将主键保存在外部表中。
              • 当一个对象引用另一个对象作为类成员时,它不会将完整的对象保存在那里。它只是指向另一个对象的指针。例如,如果两条边引用同一个顶点,则不会出现数据重复。
              • 是的,谢谢,即使我只保存了名称 - 我如何检索完整的顶点而不将它们保存在另一个将名称映射到顶点的地图中。
              • 您可能有另一个映射用于通过名称快速查找顶点作为辅助实用程序,但一般来说,图的实现通常具有相互直接引用的顶点和边。这样你就可以编写一个简单地通过访问一个顶点并跟随它的边来遍历图形的方法。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-06-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多