图是一种数据结构,有关图的算法是计算机科学中基础性的算法。这个论述恰如其分。

图的基本算法包括图的表示方法和图的搜索方法。图的搜索技术是图算法领域的核心,有序地沿着图的边访问所有顶点,可以发现图的结构信息。

1、图的表示方法:

给定图G=(V,E),其中V表示图的点、E表示图的边,V[G]表示图G的点集合,E[G]表示图G的边集合。图的表示方法主要有邻接表和邻接矩阵两类,均可用于有向图和无向图。有向图,两个顶点间有方向,是单向边,而无向图两个顶点之间是双向边。

1)邻接表

当图G中|E|远小于|V|2时,即为稀疏图,适用邻接表表示。图G=(V,E)的邻接表表示由一个包含|V|个列表的数组Adj所组成,其中每个列表对应于V中的一个顶点。对于每一个u∈V,邻接表Adj[u]包含所有满足(u,v)∈E的顶点v。即Adj[u]中包含图G中所有和顶点u相邻的顶点,如果是有向图,则是包含u指向的顶点。每个邻接表中的顶点一般以任意顺序存储。

如果G是一个有向图,则邻接表的长度之和为|E|,u指向v为一条边;如果G是一个无向图,则邻接表的长度之和为2|E|,u和v双向两条边。

邻接表的存储空间为⊙(V+E),如果是图边是加权的,可以把权值w(u,v)存储在相应数组中表示。

2)邻接矩阵

当图G中|E|接近|V|2时,即为稠密图,或者需快速判断两个顶点间是否存在连接边时,适用邻接矩阵表示。图G=(V,E)的邻接矩阵表示,假定各顶点编号从1,2,…,|V|,那么G的邻接矩阵为一个|V|X|V|的矩阵A=(aij),满足:

aij=1,如果(i,j)∈E;aij=0,如果(i,j)∉E;在有向图中属于E[G]边集合是从i到j的指向。

邻接矩阵的存储空间为⊙(V2),与图中的边数无关。如果是无向图,矩阵A的转置矩阵是自己,可以只存储对角线及对角线以上部分,存储空间节省近一半。如果图边是加权的,可以在相应矩阵行列点上存储权值。如果是非加权图,存储邻接矩阵的每个元素时,可以只用一个二进制位表示,而不必用一个字的空间。

2、图的搜索方法:

1)广度优先搜索

广度优先搜索(breadth-first search,BFS)的思想核心就是沿着点搜索图构建一颗广度优先树,发现图结构信息。给定图G=(V,E)和特定源顶点S,BFS探索G的边,发现所有S可达的顶点,并计算出S到达所有顶点之间的距离(最少边数),对有向图和无向图都适用。BFS首先发现和S距离为k的所有顶点,在发现距离为k+1的其他顶点。BFS就是找出和u点存在边关系的所有顶点后才会出发选择下一个顶点。

重点描述下BFS算法,很重要一点就是源点S到所有顶点的距离也得出,且是最短路径最少边数,这个算法导论中给出了3个引理1个推论1个定理来证明。细细品味算法导论中这些证明,有助于理解算法,同时也有助于提升数学证明逻辑,也有助于理解普适性的思想,如动态规划的分治和最优解。算法导论中给出的算法,一般行文结构是:定义、算法描述、案例、性能分析、性质或正确性证明。正确性证明中就涉及到数学基础知识基本公理基础定义和归纳推理的证明方法及普适思想,反过来说,只有掌握数学才能更好设计具有优性能和正确性的计算机算法。

BFS算法的主要变量描述:

color[u],记录顶点的状态,用白色、灰色、黑色指示,未搜索到顶点为白色,搜索到但还未完全把有边关系的顶点都搜索到的为灰色,搜索到所有有边关系的顶点的黑色;

π[u],记录u顶点的父顶点;

d[u],记录源点s到顶点u的距离,最短路径最少边数;后面带权的图最短路径就不是DFS算法了。

图G(V,E)用邻接表存储。

BFS_Func(G,s){

//第一步:初始化所有顶点,不含源顶点s

for eachvertex u∈V[G]-{s}

do color[u]=white

d[u]=∞

π[u]=null

//第二步:初始化源顶点s

color[s]=gray

d[u]=0

π[u]=null

//第三步:初始化先进先出队列(FIFO),源顶点(白色变灰色)入队列

Q=null

Enqueue(Q,s)

//第四步:DFS搜索

while Q≠null  //还有灰色顶点

do u=Dequeue(Q)  //出列,第一个是源顶点s

for each v∈Adj[u]  //邻接表中u顶点存储的具有边关系的顶点

//白色顶点才入列,确保顶点只有一次入列机会,这个就是通过color这个指示变量来满足

do if color[v]=white

then color[v]=gray //顶点变灰色,表明已搜索到,但未搜索其相关所有顶点

d[v]=d[u]+1  //距离加1

π[v]=u   //顶点v的父顶点是u

Enqueue(Q,v)

color[u]=black //顶点u都搜索完成,变黑色

}

每次阅读算法导论中的算法,都感觉到精炼、清晰,相当到位。从算法中不难得出DFS的时间运行性能是线性的,为O(V+E),虽然是双层循环,但在循环中,顶点为白色只有一次机会,所以就是V[G]顶点数,而扫描所有邻接表的次数就是图的边数E[G]。

最精彩的当然还是DFS取得的距离是最短路径。这里不赘述。

2)深度优先搜索

深度优先搜索(deepth-first search,DFS)的思想核心就是沿着边搜索图构建深度优先树林。DFS产生多个源顶点,形成多颗深度树,从一个顶点出发,从邻接表中发现第一条边的相关顶点,接着探索相关顶点邻接表中的边和相关顶点,当最后一个顶点没有边时就结束,回溯父结点的新一条边开始探索。DFS构建的深度树互不相交。应该来说BFS适用于寻找最短路径,而DFS适用于发现图的结构信息,如图的边类型。

DFS算法中的括号定理,和编译原理中语法分析思想一致。DFS给出一个括号定理和一个后裔区间的嵌套,从而得出白色路径原理。定理、推论结合DFS设计的算法都很好理解。

白色路径定理:在一个有向图或无向图G=(V,E)的深度优先森林中,顶点v是顶点u的后裔,当且仅当在搜索过程中在时刻d[u]发现u时,可以从顶点u出发,经过一条完全由白色顶点组成的路径到达v。定理的证明中,采用了综合法和分析法。

——综合法是一种从题设到结论的逻辑推理方法,也就是由因导果的证明方法;

——分析法是一种从结论到题设的逻辑推理方法,也就是执果索因法的证明方法;分析法的证明路径与综合法恰恰相反。

关于括号定理和后裔区间定理以及白色路径定理,都关系到算法中很关键的一个指标变量设计,就是时间戳。这里先谈下DFS发现图结构构建深度优先森林时关于边的分类。边的分类及性质是DFS一个重要应用点。

根据在图G上进行深度优先搜索所产生的深度优先森林Gπ,可以把图的边分类四种类型:

——树边(treeedge),是深度优先森林Gπ中的边。如果顶点v是在探寻边(u,v)时被首次发现,那么(u,v)就是一条树边;

——反向边(backedge),是深度优先树中,连接顶点u到它的某一祖先顶点v的那些边。有向图中可能出现的自环也被认为是反向边;

——正向边(forwardedge)是指深度优先树中,连接顶点u到它的某个后裔v的非树边(u,v)。

——交叉边(crossedge)是其他类型的边,存在于同一颗深度优先树中的两个顶点之间,条件是其中一个顶点不是另一个顶点的祖先。交叉边也可以在不同的深度优先树的顶点之间。

这样的一个分类对着DFS算法和案例才好理解,但看表述也基本能明白。在DFS算法中,可以对搜索到的边进行分类,分类核心思想是对于每条边(u,v),当该边被第一次搜索到时,可根据所到达的顶点v的颜色:第一类白色的v顶点,表明是一条树边;第二类灰色的v顶点,表明是一条反向边;第三类黑色的v顶点,表明是一条正向边或交叉边。这里的理解关系到DFS算法中另一个重要的指标变量设计,就是颜色。对于无向图进行深度优先搜索时边的分类,引申出一个定理:在对一个无向图G进行深度优先搜索的过程中,G的每一条边要么是树边,要么是方向边。

有时对算法导论中的定理和性质证明过程理解有点头疼,不过细看下去收获很多。以这个定理的证明来说,首先要掌握无向图的性质(边的双向性质,在邻接表中使存储两次),其次要理解DFS过程中边的分类算法,才能理解这个定理,从而应用该定理。

现在介绍下DFS算法,首先描述下主要的变量:

π[v]表示顶点v的父节点;

color[v] 表示顶点v的状态,开始为白色,第一次发现为灰色,结束该点所有边的搜索为黑色;

时间戳d[v]和f[v],在搜索过程中顶点v第一次被发现时(设为灰色)所记录的时间戳为d[v],当结束检查v的邻接表时(设为黑色)所记录的时间戳为f[v];

时间戳的直在1和2|V|之间取整,对|V|个顶点中的每一个,都有一个发现时间和完成时间,d[v]<f[v],顶点v在时刻d[v]之前是白色,在时刻d[v]和f[v]之间是灰色,f[v]之后是黑色。这个关系就是之前定理和推论的重要基础。

DFS_Func(G){

//第一步初始化所有顶点

for each vertex u∈V[G]

do color[u]=white

π[u]=null

time=0;

//第二步多源顶点出发深度搜索

for each vertex u∈V[G]

do if color[u]=white

then DFS_Visit(u)

}

DFS_Visit_Func(u){

Color[u]=gray //第一次发现,设为灰色

time=time+1

d[u]=time //发现事件的时间戳

for each v∈Adj[u]  //找出边(u,v)

do if color[v]=white //顶点v第一次发现

then π[v]=u //u是v的父结点

DFS_Visit_Func(v)  //沿着边找出顶点v的边

color[u]=black  //顶点u探索结束,设为黑色

f[u]=time+1

}

结合图示案例可以更好理解DFS算法,尤其是递归调用。DFS算法时间性能是⊙(V+E)。

深度优先搜索的应用之一:拓扑排序。

对于有向无回路图,DFS执行中使没有反向边的,这个算法导论中给出引理并证明。有向无回路图用于说明事件的先后次序,DFS后形成一个顶点序列,即是拓扑排序。对有向无回路图无反向边的证明,采用了对命题的题设和结论的反证法。

深度优先搜索的应用之二:强连通分支

有向图G=(V,E)的一个强连通分支就是一个最大顶点集合C ⊆V,对C中的每一对顶点u和v,有uàv和vàu;即顶点u和v之间是互相可达的。强连通分支是一个有向无回路图,导论中有引理并证明。

在寻找图G=(V,E)的强连通分支算法中,提到了G的转置,即GT=(V,ET),ET={(u,v):(v,u)∈E}。ET 就是有G中边改变方向所得。在给定图G的邻接表表示情况下,建立GT 需要O(V+E)时间。G和GT有着完全相同的强连通分支。导论中证明了转置后的图GT可以计算出有向图G的强连通分支。

把有向图分解成强连通分支,再运行算法,即先分治取得解再组合解。最重要的是强连通分支的定义和性质满足解组合。

算法导论之图的基本算法相关推荐

  1. Java实现算法导论中图的广度优先搜索(BFS)和深度优先搜索(DFS)

    对算法导论中图的广度优先搜索(BFS)和深度优先搜索(DFS)用Java实现其中的伪代码算法,案例也采用算法导论中的图. import java.util.ArrayList; import java ...

  2. 算法导论之图的最小生成树

    引出最小生成树,是提到电子线路设计时,要把数个元件的引脚连接在一起,使其电位相同.使n个引脚互相连通,可以使用n-1条连接线,每条连接线连接两个引脚.寻求连接线最少的方案,是最小生成树的应用.将电子线 ...

  3. 算法导论中C语言代码,算法导论-学习笔记与进度

    算法导论 阅读进度 第一部分 基础知识 第一章 计算中算法的角色 Done 1.1 算法 输入与输出 算法可以解决哪些问题 数据结构 技术 一些比较难的问题 1.2 作为一种技术的算法 效率 算法和其 ...

  4. 算法导论 pdf_学习数据结构和算法最好的书是什么?

    ----------- 通知:如果本站对你学习算法有帮助,请收藏网址,并推荐给你的朋友.由于 labuladong 的算法套路太火,很多人直接拿我的 GitHub 文章去开付费专栏,价格还不便宜.我这 ...

  5. 算法导论——24.2 DAG最短路径算法java实现

    介绍 Bellman-Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题,但是对于DAG,可以有更加简化的算法去计算,使得时间复杂度更低. 针对DAG的特点,以拓扑排序为基础,提出了解 ...

  6. 算法基础:图的相关算法知识笔记

    一.图的相关算法 1.图的分类知识 如下图: 2.生成树概念 对连通图进行遍历,过程中所经过的边和顶点的组合可看做是一棵普通树,通常称为生成树. 连通图的生成树具有这样的特征:边的数量 = 顶点数 - ...

  7. 算法导论-上课笔记7:贪心算法

    文章目录 0 前言 1 活动选择问题 1.1 活动选择问题的最优子结构 1.2 贪心选择 1.3 递归贪心算法 1.4 迭代贪心算法 2 贪心算法原理 2.1 贪心选择性质 2.2 最优子结构 2.3 ...

  8. 动态分区分配的“首次适应算法_动态图划分复制算法:Leopard

    数据管理和系统实现课程上要分享的论文:<LEOPARD: Lightweight Edge-Oriented Partitioning and Replication for Dynamic G ...

  9. 【转】算法导论学习笔记 一 分治算法

    分治策略是一种常见的算法.在分治策略中,我们递归的求解一个问题,在每层递归中应用如下三个步骤: 1. 分解,将问题分解成规模更小但解决方案相同的子问题 2. 解决,递归的求解子问题,如果子问题足够小则 ...

最新文章

  1. Java 多线程编程之 notify notifyAll wait lock unlock 算法
  2. Swift 泛型函数补充
  3. 第18条:接口优于抽象类
  4. mysql多图怎么同时上传,小程序如何同时上传多张图片?
  5. javascript --- vue中简单的模板渲染
  6. web编程 模块1 html,PYcore python programming笔记C20 Web编程
  7. TCP传输过程中丢包问题
  8. python数据分析的步骤排序_Python数据分析
  9. VMware 虚拟机安装OSX el capitan 11.12
  10. Java中HashMap和TreeMap的区别
  11. 对JMETER组件的认知
  12. 带checkbox的ListView实现(一)——数据与渲染完全分离的传统实现方式
  13. CVPR2022 Oral | CosFace、ArcFace的大统一升级,AdaFace解决低质量图像人脸识
  14. UVA 1590 IP Networks
  15. HTML自动弹出窗口代码
  16. 富邦速配民营银行管理层动荡浪潮之下,振兴银行再迎新帅
  17. EXCEL的功能整理(一)
  18. 计算机毕业设计springboot基于疫情背景下的新型点餐送餐系统bpe1s源码+系统+程序+lw文档+部署
  19. 虚拟机中的linux怎么进入命令方式
  20. 4kw机柜无通道封闭CFD模拟分析及优化(下篇) -孙长青

热门文章

  1. Java基础:Java异常处理
  2. c语言指针底层实现,C语言二级指针底层实现
  3. supmap java_SuperMap iServerJava安装与出图必读之Windows操作系统篇
  4. 客户端手册_山东省税务局社保费管理客户端企业缴费操作手册
  5. 我的世界java测试版下载_我的世界中国版PC不删档测试版_网易我的世界JAVA版测试版单机游戏下载...
  6. java $ 用法_Java的Volatile实例用法及讲解
  7. c语言宏定义_掌握C语言,中文编程不是梦
  8. 洛谷P2068 统计和题解
  9. mysql的学习笔记(四)
  10. Kotlin 在kotlin内使用Java的一些注意(长篇)