图结构基础入门,邻接表与邻接矩阵实现

223 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1 什么是图结构?

图是由节点和边组成的集合,是一种数据结构表示。可以将其符号化定义为:G=(V,E)G=(V,E)

其中V={v1,v2,.....,vn}V=\{v_1,v_2,.....,v_n\}表示节点的集合,E={e1,e2,.....,em}E=\{e_1,e_2,.....,e_m\}表示边的集合,eije_{ij}可以表示为节点viv_i到节点vjv_j的边。

2 图的一些概念:

术语含义
顶点图中的某个结点
顶点之间连线
相邻顶点由同一条边连接在一起的顶点
一个顶点的相邻顶点个数
简单路径由一个顶点到另一个顶点的路线,且没有重复经过顶点
回路第一个顶点和最后一个顶点的相同的路径
无向图图中所有的边都没有方向
有向图图中所有的边都有方向
无权图图中的边没有权重值
有权图图中的边带有一定的权重值

3 图的表示方式

image.png

图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]