图
一、图的定义
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E)。其中G表示图,V表示顶点的集合,E表示边的集合。
线性表中把数据元素叫做元素,树中将数据元素叫结点,图中数据元素叫顶点。
二、有向图和无向图
图可以分为有向图和无向图。有向图中的边具有方向,从一个顶点指向另一个顶点,表示一个单向关系。无向图中的边没有方向,连接两个顶点,表示双向关系。
1、无向边和无向图
如果两个顶点之间的边无方向,则称这条边为无向边,如果图中任意两个顶点之间的边都是无向边,则这个图被称为无向图。
2、有向边和有向图
如果两个顶点之间的边有方向,则称这条边为无向边,也称为弧(有向边的起点叫弧头,终点叫弧尾),如果图中任意两个顶点之间的边都是有向边,则这个图被称为有向图。
三、稀疏图和稠密图
根据图中边的数量,图可以分为稀疏图和稠密图。稀疏图中边的数量相对较少,顶点之间的连接比较稀疏。稠密图中边的数量较多,顶点之间的连接较为紧密。
四、完全图
完全图是指每个顶点都与其他顶点直接相连的图。对于有向图,完全图要求每个顶点都有有向边指向其他顶点,并且每个顶点也接受其他顶点的有向边。完全图分为有向图和无向图。并且完全图一定是稠密图。
五、简单图
是指图中没有自循环(边的起始顶点和结束顶点相同,比如:顶点1到顶点1有一条边) 和多重边(两个顶点之间存在多条边,比如:顶点1到顶点2有多条边)的图。
六、度
度是图中顶点的一个重要概念,它表示某个顶点与其他顶点直接相连的边的数量。对于有向图,度分为入度和出度,分别表示指向该顶点的边数和从该顶点指出的边数。
七、权
是指图中边的附加信息,可以表示边的权重、距离、成本等
八、路径
路径是图中一系列连续的边,连接了两个顶点。路径可以是有向的和无向的。相关算法就是求两个顶点之间的最短路径。比如Dijkstra算法、Bellman-Ford算法、Floyd算法、Dijkstra+Heap算法、SPFA算法都是求最短路径的算法。
九、连通图和连通分量
连通图是指图中任意两个顶点之间都存在一条路径。如果图可以划分为多个子图,使得子图内部的顶点连通,而子图之间没有连接,那么这些子图就称为连通分量。
在有向图中,如果对于任意两个顶点a和b,从a到b和从b到a都存在路径,则称它为强连通图,有向图中的极大强连通子图被称为这个有向图的强连通分量。
十、生成树
生成数是连通图的一个子图,它是一棵树(没有循环的连通图),并且包含了图中的所有顶点。生成树可以用于图的遍历、最小生成树等问题的解决。常见最小生成树算法有Kruscal和Prim,其中Kruscal用到了并查集。
邻接矩阵
一、临接矩阵的概念
由于图是由顶点和边(或弧)两部分组成的。顶点可以用一个一维的顺序表来存储,但是边(或弧)由于是顶点与顶点之间的关系,一维搞不定,所以可以考虑用一个二维的顺序表来存储,而二维的顺序表就是一个矩阵。
对于一个有n个顶点的图G,临接矩阵是一个n*n的方阵(方针就是行列数想等的矩阵)。对于临接矩阵而言,不需要去考虑有向的还是无向的,统一都可以理解成有向的,因为有向图可以兼容无向图,对于无向图而言,只不过这个矩阵是按照主对角线对称的,因为A到B有边,则必然B到A有边。
对带权图和无权图,临接矩阵的表示略有差别,接下来分别讨论。
未完。。。
实现图共两种方法:
- 邻接矩阵
- 邻接表
以下代码用邻接矩阵实现
inf = -1
class Graph:
def __init__(self, n):
self.n = n # n个顶点
self.edges = [] # 邻接矩阵
for i in range(n):
L = []
for j in range(n):
L.append(inf)
self.edges.append(L)
def addEdge(self, u, v, w):
"""
向邻接矩阵中添加权重
u: 第u行
v: 第v列
w: 权重
"""
self.edges[u][v] = w
def printGraph(self):
"""
打印邻接矩阵
"""
for i in range(self.n):
for j in range(self.n):
print(self.edges[i][j], end=" ")
print('')
def test():
g = Graph(5)
g.addEdge(0, 1, 1)
g.addEdge(0, 2, 3)
g.addEdge(1, 2, 2)
g.addEdge(2, 3, 7)
g.addEdge(3, 4, 9)
g.addEdge(4, 0, 4)
g.addEdge(4, 2, 5)
g.printGraph()
test()
以下代码用邻接表实现图
class EdgeNode:
"""
有向边节点
"""
def __init__(self, v, w):
self.vertex = v # 弧尾
self.weight = w # 权重
class VertexNode:
"""
有向边节点
"""
def __init__(self, v):
self.vertex = v # 弧头
self.edges = [] # 所有弧尾组成的列表
class Graph:
"""
有向图类
"""
def __init__(self, n):
self.n = n #有n个顶点
self.nodes = [VertexNode(i) for i in range(n)] # 初始化n个弧头节点
def addEdge(self, u, v, w): #u: 弧头 v: 弧尾 w: 权重
"""
将指定的弧尾节点和权重添加到指定的弧头
"""
self.nodes[u].edges.append(EdgeNode(v, w))
def printGraph(self):
"""
输出图节点
"""
for i in range(self.n):
print("Vertex: ", i, end=":")
for e in self.nodes[i].edges:
print(e.vertex, '(', e.weight, ')', end=" ")
print("")
def test():
g = Graph(5)
g.addEdge(0, 1, 4)
g.addEdge(0, 2, 2)
g.addEdge(1, 2, 3)
g.addEdge(2, 3, 4)
g.addEdge(3, 4, 2)
g.printGraph()
test()