本文已参与「新人创作礼」活动,一起开启掘金创作之路。
1 什么是图结构?
图是由节点和边组成的集合,是一种数据结构表示。可以将其符号化定义为:
其中表示节点的集合,表示边的集合,可以表示为节点到节点的边。
2 图的一些概念:
| 术语 | 含义 |
|---|---|
| 顶点 | 图中的某个结点 |
| 边 | 顶点之间连线 |
| 相邻顶点 | 由同一条边连接在一起的顶点 |
| 度 | 一个顶点的相邻顶点个数 |
| 简单路径 | 由一个顶点到另一个顶点的路线,且没有重复经过顶点 |
| 回路 | 第一个顶点和最后一个顶点的相同的路径 |
| 无向图 | 图中所有的边都没有方向 |
| 有向图 | 图中所有的边都有方向 |
| 无权图 | 图中的边没有权重值 |
| 有权图 | 图中的边带有一定的权重值 |
3 图的表示方式
图1 有向图
1、邻接矩阵表示
如图1所示,可以将邻接矩阵表示为:
1 2 3 4 5 6 7
1[0 0 1 0 1 1 1]
2[0 0 0 0 1 1 0]
3[0 0 0 1 1 0 0]
4[0 0 0 0 0 0 0]
5[0 0 0 0 0 1 0]
6[0 0 0 0 0 0 0]
7[0 0 0 0 0 0 0]
参考代码:
class Graph:
def __init__(self,mat,unconn=0):
vnum=len(mat)
for x in mat:
if len(x)!=vnum:
raise ValueError("参数错误")
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 v<0 or v>=self._vnum
#添加边
def add_edge(self,vi,vj,val=1):
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi)+" or "+str(vj)+"不是有效的顶点")
self._mat[vi][vj]=val
#获取边的值
def get_edge(self,vi,vj):
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi)+" or "+str(vj)+"不是有效的顶点")
return self._mat[vi][vj]
#获得一个顶点的各条出边
def out_edges(self,vi):
if self._invalid(vi):
raise ValueError(str(vi)+"不是有效的顶点")
return self._out_edges(self._mat[vi],self._unconn)
@staticmethod
def _out_edges(row,unconn):
edegs=[]
for i in range(len(row)):
if row[i]!=unconn:
edegs.append((i,row[i]))
return edegs
def __str__(self):
return "[\n"+",\n".join(map(str,self._mat))+"\n]"+"\nUnconnected: "+str(self._unconn)
2、邻接表表示
(1)图中顶点用一个一维数组存储,另外,对于顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点vi的边表,有向图称为顶点vi作为弧尾的出边表。
根据图1表示为邻接表:
1->3->5->6->7
2->6
3->4->5
4
5->6
6
7
参考代码:
class GraphAL(Graph):
def __init__(self,mat=[],unconn=0):
vnum = len(mat)
for x in mat:
if len(x) != vnum:
raise ValueError("参数错误")
self._mat=[Graph._out_edges(mat[i],unconn) for i in range(vnum)]
self._unconn = unconn
self._vnum = vnum
#添加顶点
#返回该顶点编号
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("不能向空图添加边")
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi)+" or "+str(vj)+"不是有效的顶点")
row=self._mat[vi]
i=0
while i<len(row):
if row[i][0]==vj:
self._mat[vi][i]=(vj,val) #如果原来有到vj的边,修改mat[vi][vj]的值
return
if row[i][0]>vj: #原来没有到vj的边,退出循环后加入边
break
i+=1
self._mat[vi].insert(i,(vj,val))
#获取边的值
def get_edge(self,vi,vj):
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi)+" or "+str(vj)+"不是有效的顶点")
for i,val in self._mat[vi]:
if i==vj:
return val
return self._unconn
# 获得一个顶点的各条出边
def out_edges(self,vi):
if self._invalid(vi):
raise ValueError(str(vi)+"不是有效的顶点")
return self._mat[vi]