用 Python 表示图(数据结构)

如何在 巨蟒中巧妙地表示 图表?(从头开始,即没有库!)什么样的数据结构(例如 dicts/tuple/dict (tuple))既快速又高效?一个人必须能够在它上面做各种各样的图 行动

正如所指出的,各种 图形表示法可能会有所帮助。如何在 Python 中实现它们?对于库,这个问题有很好的答案。

133482 次浏览

NetworkX 是一个非常棒的 Python 图形库。你将很难找到你需要的东西,它已经做不到。

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

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

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

其次,问题是顶点和边是否应该只用存在性来表示,或者它们是否带有一些额外的信息。

从 Python 内置数据类型的角度来看,其他地方包含的任何值都表示为对目标对象的(隐藏的)引用。如果它是一个变量(即命名引用) ,那么名称和引用总是存储在(内部)字典中。如果您不需要名称,那么引用可以存储在您自己的容器中——在这里,巨蟒名可能总是作为抽象用于 名单

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

表示矩阵的另一种方法是由标准模块 array实现的数组——相对于存储类型,同构值受到更多限制。元素直接存储值。(该列表存储对值对象的引用)。通过这种方式,可以提高内存效率,并且对值的访问速度也更快。

有时,您可能会发现像 bytearray这样的限制性表示更加有用。

尽管这是一个有点老的问题,我想我会给出一个实际的答案,任何人偶然发现这一点。

假设您以元组列表的形式获取连接的输入数据,如下所示:

[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]

我发现,对于 Python 中的图形来说,最有用、最有效的数据结构是 集合的命令。这将是我们的 Graph类的底层结构。您还必须知道这些连接是弧形(定向连接,单向连接)还是边缘(无定向连接,双向连接)。我们将通过向 Graph.__init__方法添加一个 directed参数来处理这个问题。我们还将添加一些其他有用的方法。

import pprint
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.items():  # python3: items(); python2: 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))

我将把它作为创建 find_shortest_path和其他方法的“读者练习”。

让我们看看它的实际效果。

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


>>> g = Graph(connections)  # undirected
>>> pretty_print = pprint.PrettyPrinter()
>>> pretty_print.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')
>>> pretty_print.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')
>>> pretty_print.pprint(g._graph)
{'B': {'D', 'C'},
'C': {'D', 'F', 'B'},
'D': {'C', 'E', 'B'},
'E': {'D', 'F'},
'F': {'E', 'C'}}


>>> g.add('G', 'B')
>>> pretty_print.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']

有两个优秀的图库 NetworkX 图表。您可以在 GitHub 上找到这两个库的源代码。您总是可以看到函数是如何编写的。但我更喜欢 NetworkX,因为它容易理解。
查看他们的代码,了解他们如何制作函数。您将获得多个想法,然后可以选择如何使用数据结构来创建图形。