图(Graph)是一种用来对某些现实问题进行建模的抽象的数学结构,这些问题从逻辑上可以被划分成一系列相互连接的节点。其中的节点称为顶点(vertex),顶点之间的连接称为边(edge)。
比如地铁线路就可以看作由图表示成的运输网络。每一个顶点都代表一个地铁站,而顶点之间的边则表示两个地铁站之间的路径。如果想知道某个站点到另一个站点的最短路径,图算法就能发挥作用。实际上,图算法可以被应用到任何类型的网络问题中。
map as graph
美国最大的15个 MSA 的地图
美国最大的15个 MSA 的 graph 表示
# edge.py
from __future__ import annotations
from dataclasses import dataclass
@dataclass
class Edge:
u: int # the "from" vertex
v: int # the "to" vertex
def reversed(self) -> Edge:
return Edge(self.v, self.u)
def __str__(self) -> str:
return f"{self.u} -> {self.v}"
上面代码中的 Edge 类表示两个顶点之间的连接(即“边”),每个顶点都由整数索引表示。其中 u 用来表示第一个顶点,v 表示第二个顶点。
这里只关注非方向性的 graph,edge 是双向的。而在有向图(digraph)中,edge 可以是单向的。reversed() 方法用来返回当前 edge 的逆向形式。
# graph.py
from typing import TypeVar, Generic, List, Optional
from edge import Edge
V = TypeVar('V') # type of the vertices in the graph
class Graph(Generic[V]):
def __init__(self, vertices: List[V] = []) -> None:
self._vertices: List[V] = vertices
self._edges: List[List[Edge]] = [[] for _ in vertices]
@property
def vertex_count(self) -> int:
return len(self._vertices) # Number of vertices
@property
def edge_count(self) -> int:
return sum(map(len, self._edges)) # Number of edges
# Add a vertex to the graph and return its index
def add_vertex(self, vertex: V) -> int:
self._vertices.append(vertex)
self._edges.append([]) # Add empty list for containing edges
return self.vertex_count - 1 # Return index of added vertex
# This is an undirected graph,
# so we always add edges in both directions
def add_edge(self, edge: Edge) -> None:
self._edges[edge.u].append(edge)
self._edges[edge.v].append(edge.reversed())
# Add an edge using vertex indices (convenience method)
def add_edge_by_indices(self, u: int, v: int) -> None:
edge: Edge = Edge(u, v)
self.add_edge(edge)
# Add an edge by looking up vertex indices (convenience method)
def add_edge_by_vertices(self, first: V, second: V) -> None:
u: int = self._vertices.index(first)
v: int = self._vertices.index(second)
self.add_edge_by_indices(u, v)
# Find the vertex at a specific index
def vertex_at(self, index: int) -> V:
return self._vertices[index]
# Find the index of a vertex in the graph
def index_of(self, vertex: V) -> int:
return self._vertices.index(vertex)
# Find the vertices that a vertex at some index is connected to
def neighbors_for_index(self, index: int) -> List[V]:
return list(map(self.vertex_at, [e.v for e in self._edges[index]]))
# Look up a vertice's index and find its neighbors (convenience method)
def neighbors_for_vertex(self, vertex: V) -> List[V]:
return self.neighbors_for_index(self.index_of(vertex))
# Return all of the edges associated with a vertex at some index
def edges_for_index(self, index: int) -> List[Edge]:
return self._edges[index]
# Look up the index of a vertex and return its edges (convenience method)
def edges_for_vertex(self, vertex: V) -> List[Edge]:
return self.edges_for_index(self.index_of(vertex))
# Make it easy to pretty-print a Graph
def __str__(self) -> str:
desc: str = ""
for i in range(self.vertex_count):
desc += f"{self.vertex_at(i)} -> {self.neighbors_for_index(i)}\n"
return desc
Graph 类聚焦于 graph 的核心角色,即将顶点用边连接起来。
_vertices 列表是 Graph 类的核心,每个顶点都会被存储在该列表中。但是之后在实际引用时会使用顶点在列表中的索引。顶点本身有可能会是非常复杂的数据类型,但其索引一定会是 int 类型,相对而言更加方便使用。
graph 数据类型可以使用 adjacency lists 方式实现,每个顶点都拥有一个列表,里面包含了这个顶点连接的其他顶点。这里使用了由 edge 组成的列表再组成的列表(_edges),每个顶点都拥有一个由 edge 组成的列表,这些 edge 表示该顶点与其他顶点的连接关系。
Graph 类中实现的方法的简单介绍:
vertex_count 属性:获取 graph 中顶点的数量
edge_count 属性:获取 graph 中边的数量
add_vertex 方法:添加一个新的孤立的顶点并返回其索引
add_edge 方法:添加一条边(双向,参数是 Edge 对象)
add_edge_by_indices 方法:通过顶点索引添加新的边(参数是边的两个顶点的索引 u、v)
add_edge_by_vertices 方法:通过顶点添加新的边(参数是边的两个顶点(Vertex)对象)
vertex_at 方法:通过特定的索引查询顶点
index_of 方法:根据顶点返回其索引
neighbors_for_index 方法:根据某个顶点的索引获取其临近的顶点(参数为顶点索引)
neighbors_for_vertex 方法:根据某个顶点获取其临近的顶点(参数为顶点对象)
edges_for_index 方法:根据某个顶点的索引获取与其连接的边(参数为顶点索引)
edges_for_vertex 方法:根据某个顶点获取与其连接的边(参数为顶点对象)
__str__ 方法:友好的方式输出整个 graph
补充测试代码:
# graph.py continued
if __name__ == "__main__":
# test basic Graph construction
city_graph: Graph[str] = Graph(["Seattle", "San Francisco", "Los Angeles", "Riverside", "Phoenix", "Chicago", "Boston", "New York",
"Atlanta", "Miami&#