数据结构与算法Python版之北大慕课笔记(五)

本文详细介绍了图的概念,包括顶点、边和权重,并讨论了图的两种主要表示方法——邻接矩阵和邻接列表。此外,文章还探讨了图在词梯问题、骑士周游问题、拓扑排序、强连通分支、最短路径问题以及最小生成树等实际问题中的应用。通过实例和算法分析,展示了如何使用Python实现这些图算法,以解决实际问题。
摘要由CSDN通过智能技术生成

一、图Graph

1. 图的基础知识

  • 图Graph是比树更为一般的结构,也是由节点和边构成的,实际上树是一种具有特殊性质的图。

  • 顶点Vertex(也称“节点Node”)是图的基本组成部分,顶点具有名称标识Key,也可以携带数据项payload。

  • 边Edge(也称“弧Arc”),作为2个顶点之间关系的表示,边连接两个顶点;边可以是无向或者有向的,相应的图称作“无向图”和“有向图”。

  • 权重Weight,为了表达从一个顶点到另一个顶点的代价,可以给边赋权。例如公交网络中两个站点之间的距离、通行时间和票价都可以作为权重。

    在这里插入图片描述

2. 图的定义

  • 一个图G可以定义为G = (V,E),其中V是顶点的集合,E是边的集合,E中的每条边e = (v, w),v和w都是V中的顶点;如果是赋权图,则可以在e中添加权重分量子图:V和E的子集。
  • 路径Path,图中的路径,是由边依次连接起来的顶点序列;无权路径的长度为边的数量;带权路径的长度为所有边权重的和。
  • 圈Cycle,圈是首尾顶点相同的路径。如果有向图不存在任何圈,则称作“有向无圈图 directed acyclic graph: DAG”。如果一个问题能表示成DAG,就可以用图算法很好地解决。

二、ADT Graph

ADT Graph的实现方法有两种主要形式:邻接矩阵adjacency matrix;邻接表adjacency list,两种方法各有优劣,需要在不同应用中加以选择。

1. 邻接矩阵

  • 矩阵的每行和每列都代表图中的顶点。

  • 如果两个顶点之间有边相连,设定行列值。无权边则将矩阵分量有就标注为1,没有就标注为0;带权边则将权重保存为矩阵分量值。

    在这里插入图片描述

  • 邻接矩阵实现法的优点是简单,可以很容易得到顶点是如何相连的。

  • 但如果图中的边数很少则效率低下,成为稀疏sparse矩阵。而大多数问题所对应的图都是稀疏的,边远远少于|v|^2 这个量级。

2. 邻接列表

  • 邻接列表可以成为稀疏图的更高效实现方案。维护一个包含所有顶点的主列表(master list),主列表中的每个顶点,再关联一个与自身有边连接的所有顶点的列表。

  • 邻接列表法的存储空间紧凑高效,很容易获得顶点所连接的所有顶点,以及连接边的信息。
    在这里插入图片描述

3. ADT Graph的实现

  • 顶点Vertex类:Vertex包含了顶点信息,以及顶点连接边信息。

    class Vertex:
        def __init__(self, key):
            self.id = key
            self.connectedTo = {
         }
    
        def addNeighbor(self, nbr, weight=0):
            self.connectedTo[nbr] = weight  # nbr是顶点对象的key
    
        def __str__(self):
            return str(self.id) + 'connectedTo:' + str([x.id for x in self.connectedTo])
    
        def getConnections(self):
            return self.connectedTo.keys()
    
        def getId(self):
            return self.id
    
        def getWeight(self, nbr):
            return self.connectedTo[nbr]
    
  • 图Graph类:Graph保存了包含所有顶点的主表。

    class Graph:
        def __init__(self):
            self.verList = {
         }
            self.numVertices = 0
    
        def addVertex(self, key):  # 新加顶点
            self.numVertices = self.numVertices + 1
            newVertex = Vertex(key)
            self.verList[key] = newVertex
            return newVertex
    
        def getVertex(self, n):  # 通过key查找顶点
            if n in self.verList:
                return self.verList[n]
            else:
                return None
    
        def __contains__(self, n):
            return n in self.verList
    
        def addEdge(self, f, t, cost=0):
            if f not in self.verList:
                nv = self.addVertex(f)  # 不存在的顶点先添加
            if t not in self.verList:
                nv = self.addVertex(t)
            self.verList[f].addNeighbor(self.verList[t], cost) # 调用起始顶点的方法添加临街边
    
        def getVertices(self):
            return self.verList.keys()
    
        def __iter__(self):
            return iter(self.verList.values())
    

三、图的应用

1. 词梯Word Ladder问题

  • 由爱丽丝漫游奇境的作者Lewis Carroll在1878年所发明的单词游戏。
  • 从一个单词演变为另一个单词,其中的过程可以经过多个中间单词,要求是相邻两个单词之间差异只能是1个字母。如FOOL变为SAGE:FOOL >> POOL >> POLL >> POLE >> PALE >> SALE >> SAGE
  • 我们的目标是找到最短的单词变换序列,采用图来解决这个问题的步骤如下:将可能的单词之间的演变关系表达为图,采用广度优先搜索BFS,来搜寻从开始单词到结束单词之间的所有有效路径,选择其中最快到达目标单词的路径。

构建单词关系图:

  • 首先是如何将大量的单词集放到图中,将单词作为顶点的标识key,如果两个单词之间仅相差1个字母,就在它们之间设一条边。

    在这里插入图片描述

  • 下图是从FOOL到SAGE的词梯解,所用的图是无向图,边没有权重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值