一、图的定义
从数学上讲,图由顶点的集V和边的集E构成,其中E中的每一条边都连接V中的两个顶点。顶点和边可以带标签,也可以不带标签。当边带有数字标签的时候,可以将这些数字看做为权重(weight),并且说这个图是一个加权图。
- 当且仅当两个顶点之前存在一条路径的时候,从一个顶点到达另一个顶点才是可达的。路径的长度就是该路径的边的数目。
- 如果从图中的每一个顶点到其他的每一个顶点都有一条路径的话,这个图就是连通的。
- 如果从图中的每一个顶点到其他的每一个顶点都有一条边的话,这个图就是完全的。
常见概念:
- 邻居:如果一个边连接了两个顶点的话,我们就说这两个顶点是相邻的,也叫做邻居
- 路径:从图的一个顶点到达另一个顶点的边的序列。
- 度数:一个顶点的度数等于连接它的边的数目。
二、图的表示
要表示图,我们需要一种方便的方法来存储顶点和连接顶点的边,常见表示方式:相邻矩阵和邻接表。
2.1 相邻矩阵
如下图所示,矩阵左边的两列数字和字母,分别包含了顶点的位置的行位置和标签,该顶点被看做是潜在的边的源顶点。矩阵上方的数字和字母,表示潜在的边的目标顶点。下图中的图有4 条边,因此,如图12.8所示,16个矩阵单元格中只有4个填写的1,分别是单元格(1,0)、(1,2)、(1,3)、(3,2)。如果该图是无向的,那么另外4个填满1 的单元格被看做是每一条边的重复(如图12.9所示)。
若边有权重,权重值可以占用矩阵单元格。那么,表示没有边的单元格则必须有一些值,而这些值不在允许的权重值范围之内。如果顶点带标签,标签可以存储到一个单独的一维数组之中。
2.2 邻接表
邻接表表示将与图相关的信息存储到列表的一个数组中,可以使用基于链表或基于数组的列表来实现。
当边有权重的时候,权重也可以作为节点的另一个数据字段而被包含,如图12.12所示。
三、图的遍历
图的遍历算法是从一个给定的顶点开始,向外移动以探索到相邻顶点的路径。这个算法的迭代性的(非递归的)版本将顶点都规划在一个单独的、临时性的集合中来访问。用于规划的集合类型将影响到访问顶点的顺序。
在遍历图的时候,有两种常用的访问顶点的顺序:
- 深度优先遍历(depth-first traversal),它使用栈作为泛型算法中的集合。使用栈的遍历过程会先在图中深入,然后回溯到另一条路径。换句话说,使用栈会使得算法从一个顶点移动到其一个邻居,然后到其邻居的另一个邻居,以此类推。
- 广度优先遍历(breadth-first traversal),它使用一个队列作为泛型算法中的集合,使用队列的遍历过程会访问与给定顶点相邻的每一个顶点,然后再深入到图中。
四、图集合的实现
4.1 LinkedDirectedGraph类的方法:
4.2实现代码
4.2.1 创建抽象集类,并保存为abstractcollection.py文件
"""
File: abstractcollection.py
"""
class AbstractCollection(object):
"""An abstract collection implementation."""
# Constructor
def __init__(self, sourceCollection = None):
"""Sets the initial state of self, which includes the
contents of sourceCollection, if it's present."""
self._size = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)
# Accessor methods
def isEmpty(self):
"""Returns True if len(self) == 0, or False otherwise."""
return len(self) == 0
def __len__(self):
"""Returns the number of items in self."""
return self._size
def __str__(self):
"""Returns the string representation of self."""
return "[" + ", ".join(map(str, self)) + "]"
def __add__(self, other):
"""Returns a new bag containing the contents
of self and other."""