在Python中表示图形(数据结构)

sha*_*k3r 93 python graph data-structures

如何在Python中整齐地表示图形?(从头开始,即没有库!)什么数据结构(例如dicts/tuples/dict(元组))将快速但内存效率高?一个人必须能够对其进行各种图形操作. 正如所指出的,各种图表表示可能会有所帮助.如何在Python中实现它们?至于图书馆,这个问题有很好的答案.





mVC*_*Chr 127

虽然这是一个有点老问题,但我想我会为任何绊倒这个的人提供一个实际的答案.

假设您将连接的输入数据作为元组列表获取,如下所示:

[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]
Run Code Online (Sandbox Code Playgroud)

我发现对Python中的图形最有用和最有效的数据结构是集合字典.这将是我们Graph班级的基础结构.您还必须知道这些连接是弧形(定向,单向连接)还是边缘(未定向,双向连接).我们将通过向方法添加directed参数来处理它Graph.__init__.我们还将添加一些其他有用的方法.

from collections import defaultdict


class Graph(object):
    """ Graph data structure, undirected by default. """

    def __init__(self, connections, directed=False):
        self._graph = defaultdict(set)
        self._directed = directed
        self.add_connections(connections)

    def add_connections(self, connections):
        """ Add connections (list of tuple pairs) to graph """

        for node1, node2 in connections:
            self.add(node1, node2)

    def add(self, node1, node2):
        """ Add connection between node1 and node2 """

        self._graph[node1].add(node2)
        if not self._directed:
            self._graph[node2].add(node1)

    def remove(self, node):
        """ Remove all references to node """

        for n, cxns in self._graph.iteritems():
            try:
                cxns.remove(node)
            except KeyError:
                pass
        try:
            del self._graph[node]
        except KeyError:
            pass

    def is_connected(self, node1, node2):
        """ Is node1 directly connected to node2 """

        return node1 in self._graph and node2 in self._graph[node1]

    def find_path(self, node1, node2, path=[]):
        """ Find any path between node1 and node2 (may not be shortest) """

        path = path + [node1]
        if node1 == node2:
            return path
        if node1 not in self._graph:
            return None
        for node in self._graph[node1]:
            if node not in path:
                new_path = self.find_path(node, node2, path)
                if new_path:
                    return new_path
        return None

    def __str__(self):
        return '{}({})'.format(self.__class__.__name__, dict(self._graph))
Run Code Online (Sandbox Code Playgroud)

我将把它作为"读者的练习"来创建一个find_shortest_path和其他方法.

让我们看看这个行动虽然......

>>> connections = [('A', 'B'), ('B', 'C'), ('B', 'D'),
                   ('C', 'D'), ('E', 'F'), ('F', 'C')]
>>> g = Graph(connections, directed=True)
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'C'},
 'C': {'D'},
 'E': {'F'},
 'F': {'C'}}

>>> g = Graph(connections)  # undirected
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'B'},
 'E': {'F'},
 'F': {'E', 'C'}}

>>> g.add('E', 'D')
>>> pprint(g._graph)
{'A': {'B'},
 'B': {'D', 'A', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.remove('A')
>>> pprint(g._graph)
{'B': {'D', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'}}

>>> g.add('G', 'B')
>>> pprint(g._graph)
{'B': {'D', 'G', 'C'},
 'C': {'D', 'F', 'B'},
 'D': {'C', 'E', 'B'},
 'E': {'D', 'F'},
 'F': {'E', 'C'},
 'G': {'B'}}

>>> g.find_path('G', 'E')
['G', 'B', 'D', 'C', 'F', 'E']
Run Code Online (Sandbox Code Playgroud)

  • 即使这个问题很老,我认为这正是我当时期待的那种答案.这个例子真的有助于解释如何在实现同步的同时保持简单.人们可以从不同的开源库中找到实现,但解释不一样.谢谢! (4认同)
  • 增加边缘重量需要什么样的修改? (2认同)
  • @pshirishreddy有趣的问题!我没想过,但我的直觉是使用`heapq` lib来堆积元组列表而不是集合.例如,图形将是一个堆的字典,如:`_graph = {'A':heapify([(0.3,'D'),(0.5,'B'),(0.75,'A'),(0.9, 'C')])}`(注意:你实际上不会像这样使用`heapify`,阅读lib的帮助),然后你可以使用`heapq`函数来插入并获得加权边. (2认同)

jte*_*ace 30

NetworkX是一个很棒的Python图形库.你很难找到你还不需要的东西.

它是开源的,所以你可以看到他们如何实现他们的算法.您还可以添加其他算法.

https://github.com/networkx/networkx/tree/master/networkx/algorithms

  • 这就是为什么NetworkX是一个很棒的资源.它是开源的,因此您可以看到他们如何实现他们的算法.您还可以添加其他算法. (7认同)
  • `graph.py --> class Graph` 大约有 2000 行代码。我只想看到他们如何使用`__iter__`。 (2认同)

pep*_*epr 7

首先,经典列表矩阵表示的选择取决于目的(关于表示你想做什么).众所周知的问题和算法与选择有关.抽象表示的选择决定了它应该如何实现.

其次,问题是顶点和边缘是否应仅仅根据存在来表达,或者它们是否带有一些额外的信息.

从Python内置数据类型的观点来看,其他地方包含的任何值都表示为对目标对象的(隐藏)引用.如果它是变量(即命名引用),则名称和引用始终存储在(内部)字典中.如果您不需要名称,那么引用可以存储在您自己的容器中 - 这里可能Python列表将始终用作列表作为抽象.

Python列表实现为动态引用数组,Python元组实现为具有常量内容的引用的静态数组(引用的值不能更改).因此,它们可以很容易地编入索引.这样,该列表也可用于实现矩阵.

表示矩阵的另一种方法是由标准模块实现的数组array- 相对于存储类型更加受限,同质值.元素直接存储值.(该列表存储对值对象的引用).这样,它可以提高内存效率,并且访问价值的速度也更快.

有时,您可能会发现有用甚至更有限的表示形式bytearray.


Vin*_*ain 6

有两个出色的图形库 NetworkXigraph。您可以在GitHub上找到这两个库源代码。您始终可以看到函数的编写方式。但是我更喜欢NetworkX,因为它易于理解。
查看其代码以了解其功能。您将获得多个想法,然后可以选择如何使用数据结构制作图形。