Leetcode部分图相关练习
一、图
1. 定义
-
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。在图中的数据元素,我们称之为顶点(Vertex),顶点集合有穷非空。在图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边集可以是空的。
-
图按照边的有无方向分为无向图和有向图。无向图由顶点和边组成,有向图由顶点和弧构成。弧有弧尾和弧头之分,带箭头一端为弧头。
-
图按照边或弧的多少分稀疏图和稠密图。如果图中的任意两个顶点之间都存在边叫做完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。
-
图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做度。有向图顶点分为入度和出度。
-
图上的边或弧带有权则称为网。
-
图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复的叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向则称为强连通图。图中有子图,若子图极大连通则就是连通分量,有向的则称为强连通分量。
-
无向图中连通且n个顶点n-1条边称为生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个有向图由若干棵有向树构成生成森林。
2. 实现有向图、无向图、有权图、无权图的邻接矩阵和邻接表表示方法
2.1 邻接矩阵
图的邻接矩阵的表示方式需要两个数组来表示图的信息,一个一维数组表示每个数据元素的信息,一个二维数组(邻接矩阵)表示图中的边或者弧的信息。
如果图有n个顶点,那么邻接矩阵就是一个n*n的方阵,方阵中每个元素的值的计算公式如下:
邻接矩阵表示图的具体示例如下图所示:
首先给个无向图的实例:
- 无向图的邻接矩阵都是沿对角线对称的
- 要知道无向图中某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;
- 对于有向图,要知道某个顶点的出度,其实就是这个顶点vi在邻接矩阵中第i行的元素之和,如果要知道某个顶点的入度,那就是第i列的元素之和。
但是,如果我们需要表示的图是一个网的时候,例如假设有个图有n个顶点,同样该网的邻接矩阵也是一个n*n的方阵,只是方阵元素的值的计算方式不同,如下图所示:
这里的wij表示两个顶点vi和vj边上的权值。无穷大表示一个计算机允许的、大于所有边上权值的值,也就是一个不可能的极限值。下面是具体示例,表示的一个有向网的图和邻接矩阵:
# 邻接矩阵
class Graph: # 基本图类,采用邻接矩阵表示
def __init__(self, mat, unconn = 0):
vnum = lem(mat)
for x in mat:
if len(x) != vnum: # 检查是否为方阵
raise ValueError("Argument for 'Graph'.")
self._mat = [mat[i][:] for i in range(vnum)] # 做拷贝
self.unconn =unconn
self._vnum = vnum
def vertex_num(self):
return self._vnum
def _invalid(self, v):
return 0 > v or v >= self._num
def add_vertex(self):
raise GraphError("Adj-Matrix does not support 'add_vertex'.")
def add_edge(self, vi, vj, val = 1):
if self._invalid(vi) or self._invalid(vj):
raise GraphError(str(vi) + 'or' + str(vj) + "is not a valid vertex.")
self._mat[vi][vj] = val
def get_edge(self, vi, vj):
if self._invalid(vi) or self._invalid(vj):
raise GraphError(str(vi) + 'or' + str(vj) + "is not a valid vertex.")
return self._mat[vi][vj]
# 返回顶点的出边
def out_edges(self, vi):
if self._invalid(vi):
raise GraphError(str(vi) + "is not a valid vertex.")
return self._out_edges(self._mat[vi], self._unconn)
@staticmethod
def _out_edges(row, unconn):
edge = []
for i in range(len(row)):
if row[i] != unconn:
edges.append((i,row[i]))
return edges
2.2 邻接表
邻接表是图的一种链式存储结构。主要是应对于邻接矩阵在顶点多边少的时候,浪费空间的问题。它的方法就是声明两个结构。如下图所示:
# 邻接表
def GraphAL(Graph):
def __init__(self, mat = [], unconn = 0):
vnum = len(mat)
for x in mat:
if len(x) != vnum: # 检查是否为方阵
raise ValueError("Argument for 'GraphAL'.")
self._mat = [Graph._out_edges(mat[i], unconn )for i in range(vnum)]
self._vnum = vnum
self._unconn = unconn
def add_vertex(self): #增加新顶点时安排一个新编号
self._mat.append([])
self._vnum += 1
return self._vnum - 1
def add_edge(self, vi, vj, val = 1):
if self._vnum == 0:
raise ValueError("Cannot add edge to empty graph.")
if self._invalid(vi) or self._invalid(vj):
raise GraphError(str(vi) + 'or' + str(vj) + "is not a valid vertex.")
row = self._mat[vi]
i = 0
while i < len(row):
if row[i][0] == vj: # 修改mat[vi][vj]的值
self._mat[vi][i] = (vj, val)
return
if row[i][0] > vj:
break
i += 1
self._mat[vi].insert(i, (vj, val))
def get_e