数据结构——图

最近开始学习王争老师的《数据结构与算法之美》,通过总结再加上自己的思考的形式记录这门课程,文章主要作为学习历程的记录。

涉及图的算法有很多,比如图的搜索、最短路径、最小生成树、二分图等。图是一种非线性数据结构。树的元素称为节点,图的元素称为顶点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-egbeCI8f-1575118643685)(E:\数据结构(python)\images\图1.jpg)]

图中的顶点可以与任何其他顶点建立连接关系。这种关系称为边。顶点相连接的边的条数称为顶点的度。

实际上,边还有方向的概念。我们把有方向的图称为“有向图”,边没有方向的图叫作“无向图”。下面是一个有向图的表示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBAmfOzI-1575118643686)(E:\数据结构(python)\images\图2.jpg)]

在无向图中有“度”这个概念,表示一个顶点有多少条边。在有向图中,度分为入度和出度。入度指有多少条边指向这个顶点。出度指有多少条边以这个顶点为起点指向其他顶点。

除此之外,还存在一种图——带权图。在带权图中,每个边都有一个权重。如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fuyro1eZ-1575118643686)(E:\数据结构(python)\images\图3.jpg)]

邻接矩阵存储方法

图最直观的一种存储方式即邻接矩阵。

对于无向图,如果顶点i与顶点j之间有边,就将 A [ i ] [ j ] A[i][j] A[i][j] A [ j ] [ i ] A[j][i] A[j][i]标记为1。

对于有向图,如果顶点i到顶点j之间,有一条箭头从顶点i指向顶点j的边,就将 A [ i ] [ j ] A[i][j] A[i][j]标记为1。

对于带权图,数组中存储相应的权值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tTLMq673-1575118643687)(E:\数据结构(python)\images\图4.jpg)]

用邻接矩阵表示图,比较浪费空间。对于无向图,其实只需要利用一半的空间。又比如,如果为稀疏图,即顶点很多,每个顶点的边并不多,就更浪费空间。但邻接矩阵也有优点,它的存储方式简单,直接,基于数组,故在获取两个顶点的关系时,非常高效。其次是方便计算,可将图的运算转换成矩阵之间的运算。

邻接表存储方法

针对上面邻接矩阵比较浪费内存空间的问题,提出了另一种图的存储方法——邻接表。

邻接表的图如下,每个顶点对应着一条链表,链表中存储的是与这个顶点相连接的其他顶点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n7my4nAM-1575118643687)(E:\数据结构(python)\images\图5.jpg)]

每个顶点对应的链表里面存储的是指向顶点。对于无向图,每个顶点的链表中存储的是跟这个顶点有边相连的顶点。

比较邻接矩阵和邻接表,我们可以得出结论:

邻接矩阵存储起来比较浪费,但是使用起来比较节省时间。邻接表存储起来比较节省空间,但是使用起来比较耗时间。

较浪费,但是使用起来比较节省时间。邻接表存储起来比较节省空间,但是使用起来比较耗时间。

什么是搜索算法?

深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构。搜索算法即在图中找出从一个顶点出发,到另一个顶点的路径。

首先创建一个无向图:

def createUDG(self,vNum,eNum,v,e):   #vNum表示图顶点数,eNum表示图的边数,v表示顶点列表,e表示邻接矩阵
    self.vNum = vNum
    self.eNum = eNum
    self.v = [None]*vNum
    for i in range(vNum):
        self.v[i] = v[i]
    self.e = [[0]*vNum] * vNum
    for i in range(eNum):
        a,b = e[i]
    m,n = self.locateVex(a),self.locateVex(b)  #locateVex返回顶点位置
    self.e[m][n] = self.e[n][m] = 1

广度优先搜索(BFS)

它是一种“地毯式”层层推进的搜索策略,即先查找离起始顶点最近的,然后是次近的,依次往外搜索。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V7yUoDmU-1575270078129)(E:\数据结构(python)\images\图6.jpg)]

其编程步骤为:

(1)建立访问标识数组visited[n]并初始化为0,n为图顶点的个数。

(2)将未访问顶点vi入队。

(3)将队首元素vi从队列中取出,依次访问它的未被访问的邻接点vj,vk,…,并将其入队。

(4)重复步骤(3),直至队列为空。

(5)改变i值,0≤i<n,跳出步骤(2)继续进行,直到i=n-1

def BFSTraverse(g):
    global visited
    visited = [False] * g.getVNum()
    for i in range(g.getVNum()):
        if not visited[i]:
            BFS(g,i)
  
 def BFS(g,i):
     q = LinkQueue()
     q.offer(i)
     while not q.isEmpty():
         u = q.poll()
         visited[u] = True
         print(g.getVex(u),end='')
         v = g.firstAdj(u)
         while v!=-1:
             if not visited[v]:
                 q.offer(v)
             v = g.nextAdj(u,v)

假设图中有V个顶点和E个边。最坏情况下,终止顶点 t 离起始顶点 s 很远,需要遍历完整个图才能找到。这个时候,每个顶点都要进出一遍队列,每个边也都会被访问一次,所以,广度优先搜索的时间复杂度是 O(V+E)。空间复杂度是 O(V)。

深度优先搜索(DFS)

深度优先搜索策略类似于“走迷宫”,随意选择一个岔路口来走,走着走着发现走不通的时候,就退回上一个岔路口,重新选择一条路继续走,直到最终找到出口。以下图为例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DHsIN6yT-1575270078130)(E:\数据结构(python)\images\图7.jpg)]

搜索起点为s,终点为t,希望在图中寻找一条以顶点s到顶点t的路径。在图中实线箭头表示遍历,虚线箭头表示回退。深度优先搜索找出来的路径,并不是顶点s到顶点t的最短路径。

实际上,深度优先搜索用的是一种著名的算法思想——回溯思想。这种思想解决问题的过程,非常适合用递归来实现。

其主要步骤:

(1)建立访问标识数组visited[n]并初始化为0,n为图顶点的个数。

(2)以未访问顶点vi为起始点访问其未访问邻接点vj。

(3)从vj出发递归进行步骤(2),直到所有邻接点均被访问。

(4)改变i值,0≤i<n,跳出步骤(2)继续进行,直到i = n-1。

代码实现:

def DFSTraverse(g):
    global visited
    visited = [False] * g.getVNum()
    for i in range(g.getVNum()):
        if not visited[i]:
            DFS(g,i)
def DFS(g,i):
    visited[i] = True
    print(g.getVex(i),end = ' ')
    v = g.firstAdj(i)
    while v!= -1:
        if not visited[v]:
            DFS(g,v)
        v = g.nextAdj(i,v)

假设图有n个顶点和m条边,当图的存储结构是邻接矩阵时需要扫描邻接矩阵的每一个顶点,其时间复杂度为O( n 2 n^2 n2)。当图的存储结构是邻接表时需要扫描每一条单链表,其时间复杂度为O(m)。

总结:广度优先搜索,通俗的理解就是,地毯式层层推进,从起始顶点开始,依次往外遍历。广度优先搜索需要借助队列来实现,遍历得到的路径就是,起始顶点到终止顶点的最短路径。深度优先搜索用的是回溯思想,非常适合用递归实现。换种说法,深度优先搜索是借助栈来实现的。

参考资料:王争《数据结构与算法之美》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值