搜索一个图是有序地沿着图的边訪问全部定点, 图的搜索算法能够使我们发现非常多图的结构信息, 图的搜索技术是图算法邻域的核心。

一、 图的两种计算机表示

1、 邻接表: 这样的方法表示稀疏图比較简洁紧凑。

typedef struct{
int adjvex;//邻接顶点的位置
struct ArcNode *next;
int weight;//边的权重
}ArcNode; typedef struct{
VertexType data;
ArcNode *firstarc;
}VNode, AdjList[MAX_VERTEX_NUM];

a、 邻接表中的顶点依照随意次序存储,每一个顶点的邻接顶点也是依照随意次序存储。

b、 邻接表中的顶点数即图中的节点数V,若G是无向图,那么全部邻接表的长度和为2E,若G是有向图,全部邻接表的长度和为E。

c、 不管有向图还是无向图,所须要的存储容量为O(V+E)。

d、 不足:确定边是否存在须要在顶点的邻接表中搜索全部顶点。

2、 邻接矩阵法:这样的方法适合稠密图。能非常快推断两个顶点是否相邻。

邻接矩阵先对图中的顶点进行编号1...|V|,编号之后,用一个|V| X |V| 矩阵来表示图。矩阵中的元素aij是否为0表示Vi和Vj之间是否有边,存储矩阵的空间为O(|V|^2)。与边数无关。

a、 在无向图的有些应用中。为了节省存储空间能够仅仅存储上三角或者下三角矩阵。

b、 边的权重能够用矩阵元素存储;

c、 对于非加权图用一个二进制位来表示是否有边能够节省存储空间。

二、 广度优先的图搜索算法

给定一个图G=(V, E)和源点s, 广度优先搜索算法系统地探寻G全部的边。从而发现从s可达的全部 的顶点。并计算s到全部这些顶点的距离(最少边数)。

该算法同一时候生成一棵根为s且包括全部可达顶点的广度优先树。

对于从s出发的随意节点。广度优先树中从s到v的路径相应G中从s到v的最短路径。算法对有向图和无向图都相同有效。

算法自始至终通过已找到和未找到顶点之间的边界向外扩展。

1、 运行过程:广度优先搜索为每一个顶点着色(白灰黑),開始的时候都是为白色。第一次碰到一个顶点的时候。称顶点被发现,此时顶点由白色变成非白色。灰色和黑色顶点都是被发现的顶点。与黑色顶点邻接的全部顶点都是已被发现的。灰色顶点能够与一些白色顶点邻接,代表已发现和未发现的边界。

2、 广度优先搜索建立了一棵广度优先树,每当一个白色顶点被发现时,该顶点与相关的边就被加入到树中。

3、 附加数据结构:存储颜色的数组color,存储父母节点的pi,以及存储从源点s到顶点的最短距离的d,存放灰色节点的先进先出队列Q。

BFS(G, s)
1 for each vertex v in {G}-{s}
2 do color[v]<--white
3 d[v]<--0
4 pi[v]<--0
5 color[s]<--gray
6 d[s]<--0
7 pi[s]<--nil
8 Q<--NULL
9 Enqueue(Q, s)
10while Q != NULL
11 do u<--Dequeue(Q)
12 for each v in Adj[u]
13 do if color[v]<--white
14 then color[v]<--gray
15 d[v]<--d[u]+1
16 pi[v]<--u
17 Enqueue(Q, v)
18 color[u]<--black

4、 广度优先搜索的结果与12行中给定顶点的邻接点的訪问顺序有关,产生的广度优先树可能不同,可是计算出来d是一样的。

5、 灰色顶点的父节点未必是黑色顶点,在11-18行中间产生灰色节点的时候。其父节点尚未变黑。

6、 执行时间:第13行的測试保证每一个节点至多仅仅入队列一次,因而也至多仅仅出队列一次。队列操作所用时间为O(V)。每一个顶点出队列时訪问其邻接表。因此每一个顶点的邻接表至多仅仅訪问一次,全部邻接表表长之和为O(E),因此总的时间复杂度为O(v+E)。

7、 对于一个图,广度优先搜索能够得到从s可达的每一个节点的距离。

8、 广度优先树:在BFS的搜索图的同一时候建立了一棵广度优先树,这棵树是由每一个结点的pi域表示。

前驱子图:对于图G=(V,E),给定原点s,其前驱子图Gpi = {Vpi。 Epi}

当中,Vpi = {v∈V, pi(v) != nil} ∪{s}

Epi = {(pi(v), v) : v∈ Vpi - {s}}

假设Vpi由从s可达的全部定点构成。那么Gpi是一棵广度优先树,且| Epi |  = | Vpi | - 1

定理: 假设DFS应用于一个有向图或无向图,该过程同一时候建立的pi域满足条件:其前驱子图 Gpi = {Vpi, Epi}是一棵广度优先树。

Print-Path(G, s, v)
1 if s = v
2 then print s
3 else if pi[v] = nil
4 then print error "no path exist"
5 else Print-Path(G, s, pi[v])
6 print v

三、 深度优先的图搜索算法

深度优先搜索所遵循的策略是尽可能深的搜索图。在深度优先搜索的过程中,对于新发现的节点。假设还有以此节点为起点而为探寻到的边。就沿着此边继续探寻下去。当节点v的全部边已被探索过或者无边可探。就回溯到以发现此节点v的节点为起点的边,这个过程一直进行到发现从原点可达的全部节点为止。假设还存在未发现的节点,就从中选一个为起点又一次開始探索。假设此重复。直到全部的节点都被探寻到为止。

1、 深度优先搜索的先辈子图形成一个由数个深度优先树组成的深度优先森林。

2、 深度优先搜索也为节点着色,最開始为白色。探寻到的时候置为灰色。结束时置为黑色。这样能够保证每一个节点仅仅存在于一棵深度优先树中。

3、 除了创建一棵深度优先树之外。DFS还为每一个节点加盖时间戳,当节点第一次被发现时(置为灰色)记下第一个时间戳d[v],当结束检查v的邻接表时(置为黑色),记下第二个时间戳f[v]。

在d[v]之前v是白色的。在f[v]之后v是黑色的。在d[v]和f[v]之间v是灰色的。

4、 深度优先搜索算法:有DFS和DFS-Visit两个过程组成

DFS(G)
1 for each vertex v in V
2 do color[v]<--white
3 d[v]<--0
4 f[v]<--0
5 pi[v]<--nil
6 time<--0
7 for each vertex u in V
8 do if color[u] = white
9 then DFS-Visit(G, u) DFS-Visit(G, u)
1 color[u]<--gray
2 time<--time+1
3 d[u]<--time
4 for each vertex v in Adj[u]
5 do if color[v] = white
6 pi[v]<--u
7 DFS-Visit(G, v)
8 color[u]<--black
9 time<--time+1
10f[u]<--time

时间复杂度为Θ(V+E)。

5、 边的分类:依据DFS产生的深度优先森林,能够将边分成四类——树边,正向边,反向边。交叉边。

6、 深度优先搜索的发现和完毕时间具有括号性质。

7、 白色路径定理: 在一个图G=(V,E)(有向或无向图)的深度优先森林中。结点v是结点u的后裔当且仅当在搜索中于d[u]时刻发现u时,能够从顶点u出发。经过一条全然由白色顶点组成的路径到达v。

8、 能够对算法DFS进行一些改动。使之遇到边时能对其进行分类。算法的核心思想在于对于每条边(u,v),当该边第一次被探寻到时。即依据所到达的结点v的颜色。来对该边进行分类(但正向边和交叉边不能用颜色区分出):

a、 白色表明它是树边;

b、 灰色说明它是反向边。

c、 黑色表示他是正向边或者交叉边。假设d[u]<d[v]。则边(u,v)就是正向边,反之就是交叉边。

9、 以上分类对于无向图来说。可能会有歧义。

在对无向图G进行深度优先搜索的过程中,G的每条边要么是树边,要么是反向边。

四、 拓扑排序

一个图的拓扑排序能够看成是图的全部顶点沿水平线排成的一个序列,使得全部的有向边均从左指向右。

1、 以下简答的算法能够对有向图进行拓扑排序:

TOPOLOGICAL-SORT(G)

a、 调用DFS(G)计算每一个节点v的f[v]。

b、 当每一个顶点完毕后。把它插入链表前端;

c、 返回由顶点组成的链表。

由于深度优先搜索的执行时间为Θ(V+E),而将|V|个顶点中的每个插入链表所占用的时间为O(1),因此进行拓扑排序的执行时间为Θ(V+E)。

上述算法能否够改成当探寻到每一个定点d[v]的时候,就将定点插入到链表的尾端呢?不行。例如以下图所看到的,假设这种话,shoes就在socks的前面了,其实。socks到shoes有一条有向边。shoes应该在socks的后面。矛盾。

2、 引理:有向图G无回路当且仅当对G进行深度优先搜索没有得到反向边。

3、 定理: TOPOLOGICAL-SORT (G) 算法可产生有向无回路图G的拓扑排序。

五、 强连通分枝

1、 在有向图中,假设不论什么两个不同的定点都相互可达。则称有向图是强连通的。一个有向图的极大强连通子图称为其强连通分枝。

2、 非常多有关有向图的算法都从分解步骤開始,这样的分解可把原始的问题分成数个子问题。当中每一个子子问题相应 一个强连通分支。构造强连通分支之间的联系也就把子问题的解决方法联系在一起,我们能够用一种称之为分支图的图来表示这 种构造。

3、 寻找图G=(V,E)的强连通分支的算法中使用了G 的转置,即E‘由G中的边改变方向后组成。若已知图G的邻接表,则建立GT所需时间为O(V+E)。G和G’有着全然同样的强连通支, 即在G中u和v互为可达当且仅当在GT中它们互为可达。

4、 下列执行时间为Θ(V+E)的算法可得出有向图G=(V,E)的强连通分支,该算法使用了两次深度优先搜索,一次在 图G上进行,还有一次在图G‘上进行:

Strongly_Connected_Components(G)

a、 调用DFS(G)以计算出每一个结点u的完毕时刻f[u];

b、 计算出G’。

c、 调用DFS(GT),但在DFS的主循环里按f[u]递减的顺序考虑各结点(和第一行中一样计算);

d、 输出第3步中产生的深度优先森林中每棵树的顶点, 作为各自独立的强连通分支。

Strongly_Connected_Component算法的思想来自于分支图GSCC = (VSCC, ESCC)的一个重要性质:

如果G的强连通分支为C1 , C2 ,..., Ck。顶点集Vscc 为{v1 , v2 ,..., vk }, 对于G的每个强连通分支Ci ,都包括一个顶点vi。如果对于某个x ∈Ci 以及某个y ∈Cj,G中包括了一条有向边(x, y) 的话,则就有一条边(vi , vj ) ∈Escc。从还有一方面看,收缩那些其关联顶点都处于G的同一强连通分支内的边,就可以得到图Gscc。

5、 引理:设C和C′是有向图G = (V, E)中的两个不同的强连通分支。设u, v ∈C, u′, v′∈C′, 并如果G中存在着一条通路u→u′,那么G中就不可能同一时候存在通路v′→ v。

6、 引理: 设C和C’为有向图G=(V,E)中的两个不同的强连通分支。

如果一条边(u,v) ∈E,当中u ∈C,v ∈C’。则f(C)>f(C’)。

推论:设C和C’为有向图G=(V,E)中两个不同强连通分支,如果存在着一条边(u, v) ∈ET, u ∈C且v ∈ C′. 那么f(C) < f(C′)。

7、 Strongly_Connected_Components(G)能正常工作的原因:

a、 第二次在GT 上运行深度优先搜索:从强连通分支C開始。f(C)是最大的,搜索从C的某个顶点x開始。訪问C 全部顶点。

b、 依据推论,GT没有从C到其它连通分支的边。根为x的树仅包括C中的顶点。

c、 接下来訪问C’。f(C’)是f(C)外最大的,与訪问C过程类似当算法第3行对GT 进行深度优先搜索时。该分支出来的不论什么边都指向已被訪问过的分支。

因此,每颗深度优先树都是一个强连通分支。

算法导论——lec 10 图的基本算法及应用的更多相关文章

  1. "《算法导论》之‘图’":深度优先搜索、宽度优先搜索(无向图、有向图)

    本文兼参考自<算法导论>及<算法>. 以前一直不能够理解深度优先搜索和广度优先搜索,总是很怕去碰它们,但经过阅读上边提到的两本书,豁然开朗,马上就能理解得更进一步. 下文将会用 ...

  2. "《算法导论》之‘图’":最小生成树(无向图)

    本文主要参考自<算法>. 加权图是一种为每条边关联一个权值或是成本的图模型.这种图能够自然地表示许多应用.在一幅航空图中,边表示航线,权值则可以表示距离或是费用.在一幅电路图中,边表示导线 ...

  3. 《算法导论》读书笔记之排序算法—Merge Sort 归并排序算法

    自从打ACM以来也算是用归并排序了好久,现在就写一篇博客来介绍一下这个算法吧 :) 图片来自维基百科,显示了完整的归并排序过程.例如数组{38, 27, 43, 3, 9, 82, 10}. 在算法导 ...

  4. 数据结构与算法系列研究七——图、prim算法、dijkstra算法

    图.prim算法.dijkstra算法 1. 图的定义 图(Graph)可以简单表示为G=<V, E>,其中V称为顶点(vertex)集合,E称为边(edge)集合.图论中的图(graph ...

  5. 算法导论--最小生成树(Kruskal和Prim算法)

    转载出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51908175 关于图的几个概念定义: 连通图:在无向图中,若任意两个顶 ...

  6. 《算法导论》读书笔记之图论算法—Dijkstra 算法求最短路径

    自从打ACM以来也算是用Dijkstra算法来求最短路径了好久,现在就写一篇博客来介绍一下这个算法吧 :) Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的 ...

  7. 《算法导论》——矩阵乘法的Strassen算法

    前言: 很多朋友看到我写的<算法导论>系列,可能会觉得云里雾里,不知所云.这里我再次说明,本系列博文时配合<算法导论>一书,给出该书涉及的算法的c++实现.请结合<算法导 ...

  8. 算法导论——lec 11 动态规划及应用

    和分治法一样,动态规划也是通过组合子问题的解而解决整个问题的.分治法是指将问题划分为一个一个独立的子问题,递归地求解各个子问题然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题不是相互 ...

  9. 算法导论——lec 12 平摊分析与优先队列

    在平摊分析中,运行一系列数据结构操作所须要的时间是通过对运行的全部操作求平均得出.反映在不论什么情况下(即最坏情况下),每一个操作具有平均性能.掌握了平摊分析主要有三种方法,聚集分析.记账方法.势能方 ...

随机推荐

  1. Gentoo Linux 学习笔记1

         Gentoo Linux是一个基于portage进行包管理的Linux发行版,最早版本始于2002年.其官方官网为http://www.gentoo.org 目前,Gentoo Linux已 ...

  2. Oracle EBS-SQL (BOM-10):检查有BOM无计划员的数据.sql

    select DISTINCT     msi.segment1 编码    ,msi.description 描述    ,msi.item_type 物料类型    ,msi.inventory_ ...

  3. 各种HelloWorld

    http://blog.csdn.net/whxaing2011/article/details/20736759 ES总结: http://www.cnblogs.com/sunxucool/p/3 ...

  4. rsyslog 读日志文件 ,当rsyslog 中断时,也会丢数据

    rsyslog 日志服务器: [root@dr-mysql01 winfae_log]# grep scan0819 wj-proxy01-catalina.out.2016-08-19 [root@ ...

  5. C语言入门(20)——使用VC2013对C语言进行调试

    软件调试过程中,有时会一些逻辑和内存访问方面的问题,如果没有调试器的帮助,找出何处代码导致这块内存被更改是一件非常麻烦的事情.恰当运用数据断点可以快速帮我们定位问题的所在. 1.VC的调试快捷键 F5 ...

  6. SVG 教程

    SVG 意为可缩放矢量图形(Scalable Vector Graphics). SVG 使用 XML 格式定义图像. 现在开始学习 SVG! <html> <body> &l ...

  7. javascript第十二课array数组

    数组的声明方式: var add=new array(元素1,元素2,元素3......); 推荐的数组声明方式: var add=[元素1,元素2,元素3,元素4....]; 数组遍历方式: 循环遍 ...

  8. libc++abi.dylib handler threw exception

    在iOS开发时,有时候遇到libc++abi.dylib handler threw exception这样的异常,  虽然在断点出加上了All Exceptions,也断到相应的代码了,但是没打印对 ...

  9. Fix The thread xxx has exited with code 259 (0×103)

    When run the test case in VS2013, you may encounter below problem After test case end, it will show ...

  10. 将类数组对象(array-like object)转化为数组对象(Array object)

    用法:Array.prototype.slice.call(array-like object) // 创建一个类数组对象 var alo = {0:"a", 1:"b& ...