如何在graph中整齐地代表Python? (从头开始,即没有库!)
什么数据结构(例如dicts / tuples / dict(元组))将是快速的还是内存效率的?
一个人必须能够做各种图形{{3在它上面。
正如所指出的,各种operations可能有所帮助。如何在Python中实现它们?
至于库,graph representations有很好的答案。
答案 0 :(得分:114)
虽然这是一个有点老问题,但我认为我会为任何绊倒这个问题的人提供实用的答案。
假设您将连接的输入数据作为元组列表获取,如下所示:
[('A', 'B'), ('B', 'C'), ('B', 'D'), ('C', 'D'), ('E', 'F'), ('F', 'C')]
我发现对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))
我会将其作为“读者练习”来创建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']
答案 1 :(得分:27)
NetworkX是一个很棒的Python图形库。你很难找到你还不需要的东西。
它是开源的,所以你可以看到他们如何实现他们的算法。您还可以添加其他算法。
https://github.com/networkx/networkx/tree/master/networkx/algorithms
答案 2 :(得分:6)
首先,经典列表与矩阵表示的选择取决于目的(您想要对表示做什么)。众所周知的问题和算法与选择有关。抽象表示的选择决定了它应该如何实现。
其次,问题是顶点和边缘是否应仅仅根据存在表达,或者它们是否带有一些额外的信息。
从Python内置数据类型的观点来看,其他地方包含的任何值都表示为对目标对象的(隐藏)引用。如果它是变量(即命名引用),则名称和引用始终存储在(内部)字典中。如果您不需要名称,那么引用可以存储在您自己的容器中 - 这里可能 Python list 将始终用作 list 作为抽象。
Python列表实现为动态引用数组,Python元组实现为具有常量内容的引用的静态数组(引用的值不能更改)。因此,它们可以很容易地编入索引。这样,该列表也可用于实现矩阵。
表示矩阵的另一种方法是由标准模块array
实现的数组 - 相对于存储类型,同质值更受限制。元素直接存储值。 (该列表存储对值对象的引用)。这样,它可以提高内存效率,并且对值的访问也更快。
有时,您可能会发现有用的更有限的表示形式,例如bytearray
。
答案 3 :(得分:5)