图的遍历和连通性

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

12
visited[v] = TRUE; Visit(v); // 访问v EnQueue(Q, v); // v入队列 while (!QueueEmpty(Q)) { DeQueue(Q, u); // 队头元素出队并置为u for(w=FirstAdjVex(G, u); w!=0; w=NextAdjVex(G,u,w)) if ( ! visited[w]) { visited[w]=TRUE; Visit(w); EnQueue(Q, w); // 访问的顶点w入队列 } // if } // while
第7章 图
7.1 基本术语 7.2 存储结构 7.3 图的遍历 7.4 图的连通性 7.5 图的应用
1
7.3
图的遍历
遍历:从已给的连通图中某一顶点出发,沿着一些边, 访遍图中所有的顶点,且使每个顶点仅被访问一次, 就叫做图的遍历,它是图的基本运算。 遍历实质:找每个顶点的邻接点的过程。 图的特点:图中可能存在回路,且图的任一顶点都可能与 其它顶点相通,在访问完某个顶点之后可能会沿着某 些边又回到了曾经访问过的顶点。 怎样避免重复访问? 解决思路:可设置一个辅助数组 visited [n ],用来标 记每个被访问过的顶点。它的初始状态为false, 在图的遍历过程中,一旦某一个顶点i 被访问,就 立即改 visited [i]为true,防止它被重复访问。 2
23
例如:
(a 19 12
b
c
d e
f
g)
a
18
14 16 27
b
7 8
5
c
3
e f
(a
g
21
d
∞ 19 ∞ ∞ 14 ∞ 18 19 ∞ 5 7 12 ∞ ∞ ∞ 5 ∞ 3 ∞ ∞ ∞ ∞ 7 3 ∞ 8 21 ∞ 14 12 ∞ 8 ∞ ∞ 16 ∞ ∞ ∞ 21 ∞ ∞ 27 18 ∞ ∞ ∞ 16 27 ∞ d e f g)
// 对v的尚未访问的邻接顶点w递归调用DFS
} // DFS
4
对于无向图,这个算法可以遍历到v顶点所在的连通 分量中的所有顶点,而与v顶点不在一个连通分量中的 所有顶点遍历不到; 对于有向图可以遍历到起始顶点v能够到达的所有顶 点。 若希望遍历到图中的所有顶点,就需要在上述深度 优先遍历算法的基础上,增加对每个顶点访问状态的 检测。 2、非连通图的深度优先搜索遍历 步骤: • 访问起始点 v及图中所有和v有路径相通的顶点。 • 如果图中尚有顶点未被访问,则以该顶点为起始点, 进行深度优先搜索遍历。重复上述过程,直至所有 5 顶点都已被访问。
// 对尚未访问的顶点调用DFS
}
7
DFS 算法效率分析: (设图中有 n 个顶点,e 条边) (1)如果用邻接矩阵来表示图,遍历图中每一个 顶点都要从头扫描该顶点所在行,因此遍历全部 顶点所需的时间为O(n2)。 (2)如果用邻接表来表示图,虽然有 2e 个表结点, 但只需扫描 e 个结点即可完成遍历,加上访问 n 个头结点的时间,因此遍历图的时间复杂度为 O(n+e)。 结 论: 稠密图适于在邻接矩阵上进行深度优先遍历; 稀疏图适于在邻接表上进行深度优先遍历。
13
BFS 算法效率分析:
• 如果使用邻接表来表示图,则BFS循环的总时间代价为 d0 + d1 + … + dn-1 = O(e),其中的 di 是顶点 i 的度。 • 如果使用邻接矩阵,则BFS对于每一个被访问到的顶点, 都要循环检测矩阵中的整整一行( n 个元素),总的 时间代价为O(n2)。
图常用的遍历: 深度优先搜索和广度优先搜索 一、深度优先搜索( DFS ) Depth_First Search
基本思想:——类似于树的先根遍历过程。 1、连通图的深度优先搜索遍历 步骤: • 访问起始点 v; • 依次从v的未被访问的邻接点出发深度优先遍历 图直至图中所有和v有路径相通的顶点都被访问 到。
3
例1: v2 v4 v8
起点 v1 v3 v5 v6
DFS 结果:
v1→ v2→ v4 →v8 → v5 → v3 →v6 →v7
v7 应退回到V8,因为 V2已有标记
void DFS(Graph G, int v) {// 从顶点v出发,深度优先遍历G visited[v] = TRUE; VisitFunc(v); for(w=FirstAdjVex(G, v);w!=0; w=NextAdjVex(G,v,w)) if (!visited[w]) DFS(G, w);
集 合 U
u0 v0
集 合 V-U
Baidu Nhomakorabea
a
例如:
19 14 16 12
b
7
5
18
c
3
e
8
g
27
d f
21
所得生成树权值和
= 14+8+3+5+16+21 = 67
22
方法:设置一个辅助数组,对当前V-U集中的每 个顶点,记录和顶点集U中顶点相连接的代价最小 的边。对每个属于V-U的顶点vi,在辅助数组中存 在一个相应的分量closedge[i-1],它包含两个域, 其中lowcost存储该边上的权。显然, closedge[i-1].lowcost=Min{cost(u,vi)|u€U} 存储方式: struct { VertexType adjvex; // U集中的顶点序号 VRType lowcost; // 边的权值 } closedge[MAX_VERTEX_NUM];
DFS与BFS之比较: (设图中有 n 个顶点,e 条边)
• 空间复杂度相同,都是O(n)(借用堆栈或队列装n个 顶点); • 时间复杂度只与存储结构(邻接矩阵或邻接表)有 关,而与搜索路径无关。 14
7.4 图的连通性
1.求无向图的生成树(或生成森林) 生成树:是一个极小连通子图,它含有图中全部顶点, 但只有n-1条边。 生成森林:由若干棵生成树组成,含全部顶点,但构 成这些树的边是最少的。 (对有向或无向图 均适用) 思考1:若对连通图进行遍历,得到的是什么? 一个极小连通子图,即图的生成树! 由深度优先搜索得到的生成树,为深度优先生成树。 由广度优先搜索得到的生成树,为广度优先生成树。 思考2:若对非连通图进行遍历,得到的是什么? 各连通分量的生成树,即图的生成森林!
2 12 ^
5
11 ^
DFS结果: ABMJLCF DE GHKI
10 ^
8 10 ^ 12 7 9 9 ^ ^
12 ^ 11 ^
18
A 子图1: F
C J
B 生 成 森 林 F
A C B M D J L E G I H K
19
L 子图2: D G 子图3: I H K
M E
注:图的生成树(生成森林)不唯一!
1 3 2 3 5 6 5 3 6 1 5 4 6 4 2 2 3 5 5 1 4 6
25
4 2
Kruskal算法的时间效率=O(elog2e)
8
二、广度优先搜索( BFS )
Breadth_First Search
基本思想:——仿树的层次遍历过程。 步骤: • 在访问了起始点v之后,依次访问 v的各个未曾访问 过的邻接点; • 然后按这些顶点被访问的先后次序依次访问它们的 邻接点,直至图中所有和V有路径相通的顶点都被访 问到。 • 若此时图中尚有顶点未被访问,则另选图中一个未曾 被访问的顶点作起始点,重复上述过程,直至图中 所有顶点都被访问到为止。
2.
求无向网的最小生成树
目标:在网络的多个生成树中,寻找一个各边权值之 和最小的生成树。即在 e 条带权的边中选取 n-1 条边 (不构成回路),使“权值之和”为最小。 典型用途: 欲在n个城市间建立通信网,则n个城市应铺n-1条线路; 但因为每条线路都会有对应的经济成本,而n个城市可 能有n(n-1)/2 条线路,那么,如何选择n–1条线路使 总费用最少? 最小生成树(MST)的 性质如下: V是顶点集,U是V的一个非空子集,若(u0, v0)是一 条最小权值的边,其中u0∈U,v0∈V-U;则:(u0, v0) 必在最小生成树上。 20
求MST有多种算法,但最常用的是以下两种:
Prim(普里姆)算法 Kruskal(克鲁斯卡尔)算法
对顶点操作 对边操作
Prim算法特点: 顶点归并,与边数无关,适于稠密网。 Kruskal算法特点:边归并,适于求稀疏网。 普里姆算法的基本思想: 在生成树的构造过程中,图中 n 个顶点分属两个集 合:已落在生成树上的顶点集 U 和尚未落在生成树 上的顶点集V-U ,则应在所有连通U中顶点和V-U中顶 点的边中选取权值最小的边。如图所示: 21
9
例1: v2 v4 v8 例2:
v1
起点 v3
BFS 结果:
v5
v6
v7
v1→ v2→ v3 → v4→v5→v6 →v7→v8
起点 BFS 结果:
v3 →v2 → v1 → v6 → v4 → v5 → v9 → v8 → v7
10
讨论: (1)在广度优先遍历中,要求先被访问的顶点其邻接点 也被优先访问,应采用何种方式记录顶点访问顺序? 答:利用一个队列结构记录顶点访问顺序,将访问的 每个顶点入队,然后,再依次出队,出队时访问其邻 接点并将邻接点入队。 (2)如何避免重复访问某个顶点? 答:创建一个一维数组visited[0..n-1](n是图中顶 点的数目),用来记录每个顶点是否已经被访问过。
v0 v3 v1 v2 16
v4
v3 v4
v4
例2:画出下图的生成森林(或极小连通子图) A C F I J L M D G E H K B
下面选用邻接表方式来求深度优先生成森林
17
0 1 2 3 4 5 6 7 8 9 10 11 12
A B C D E F G H I J K L M
1 0 0 ^ 4 ^ 3 ^ 0 ^ 7 6 6 ^ 11 6 0 1
15
例1 :画出下图的生成树。
v0 v2 v3 v4 v4 DFS v1
0 邻 1 接 2 表 3 4
v0 v3 v4 v2 v1
v0 v1 v2 v3 v4 v0
3 4 4 4 3
1 2 3 2 2
^ 0 1 0 1 ^ ^ ^ ^
v0 v2 v3 v4
v1
生 成 树
v1 v2
BFS
生 成 树
v4
例2: 2 c 7 h
0 1 2
0 3 d
a
4 e 8 k
3 4
1 b 5 f
6 g
5
6
7
8
访问标志: F T F T F TF T F T F T F T F TF T 访问次序: a c h d k f e b g
6
void DFSTraverse(Graph G, Status (*Visit)(int v)) { // 对图 G 作深度优先遍历。 VisitFunc = Visit; for (v=0; v<G.vexnum; ++v) visited[v] = FALSE; // 访问标志数组初始化 for (v=0; v<G.vexnum; ++v) if (!visited[v]) DFS(G, v);
b
c
closedge
0
1
2
3
4
5
6
Adjvex Lowcost
d a e c 19 12 7 5
d 3
e 8
a 14
a e 21 18 16 d
24
Prim算法的时间效率=O(n2)
克鲁斯卡尔算法的基本思想: 考虑问题的出发点: 为使生成树上边的权值之和达到 最小,则应使生成树中每一条边的权值尽可能地小。 具体做法: 先构造一个只含 n 个顶点的子图 SG,然后从 权值最小的边开始,若它的添加不使SG 中产生回路, 则在 SG 上加上这条边,如此重复,直至加上 n-1 条边 为止。 6 1 5
(3)广度优先搜索有回退的情况吗?
11
答:广度优先搜索是一种分层的搜索过程,每向前走 一步可能访问一批顶点,不像深度优先搜索那样有回 退的情况。因此广度优先搜索不是一个递归的过程, 其算法也不是递归的。 void BFSTraverse(Graph G, Status (*Visit)(int v)){ for (v=0; v<G.vexnum; ++v) visited[v] = FALSE; //初始化访问标志 InitQueue(Q); // 置空的辅助队列Q for ( v=0; v<G.vexnum; ++v ) if ( !visited[v]) { // v 尚未访问 … ……… } } // BFSTraverse
相关文档
最新文档