基本概念 图的存储结构 图的遍历 生成树 最短路径 拓扑排序

74
基基基基 基基基基 基基基基基基 基基基基基基 基基基基 基基基基 基基基 基基基 基基基基 基基基基 基基基基 基基基基 7 7

Upload: elan

Post on 19-Jan-2016

199 views

Category:

Documents


1 download

DESCRIPTION

第 7 章 图. 基本概念 图的存储结构 图的遍历 生成树 最短路径 拓扑排序. 7.1 图的基本概念. 图的定义: 一个图 G 是由两个集合 V 和 E 组成, V 是有限的非空顶点集, E 是 V 上的顶点对所构成的边集,分别用 V ( G )和 E ( G )来表示图中的顶点集和边集。用二元组 G= ( V , E )来表示图 G 。. 有向图与无向图 若图 G 中的每条边都是有方向的,则称 G 为有向图。有向边也称为 弧 。若图 G 中的每条边都是没有方向的,则称 G 为无向图。 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

基本概念基本概念 图的存储结构图的存储结构 图的遍历图的遍历 生成树生成树 最短路径最短路径 拓扑排序拓扑排序

第第 77 章 图章 图

Page 2: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.1 7.1 图的基本概念图的基本概念

图的定义:图的定义:一个图一个图 GG 是由两个集合是由两个集合 VV 和和 EE 组组成,成, VV 是有限的非空顶点集,是有限的非空顶点集, EE 是是 VV 上的上的顶点对所构成的边集,分别用顶点对所构成的边集,分别用 VV (( GG )和)和EE (( GG )来表示图中的顶点集和边集。用)来表示图中的顶点集和边集。用二元组二元组

G=G= (( VV ,, EE )来表示图)来表示图 GG 。。

Page 3: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

有向图与无向图有向图与无向图 若图 若图 GG 中的每条边都是有中的每条边都是有方向的,则称方向的,则称 GG 为有向图。有向边也称为为有向图。有向边也称为弧弧。。若图若图 GG 中的每条边都是没有方向的,则称中的每条边都是没有方向的,则称 GG为无向图。为无向图。

完全图完全图 对有 对有 nn 个顶点的图,若为无向图且个顶点的图,若为无向图且边数为边数为 n(n-1)/2n(n-1)/2 ,则称其为,则称其为无向完全图无向完全图;;若为有向图且边数为若为有向图且边数为 n(n-1) n(n-1) ,则称其为,则称其为有有向完全图。向完全图。

邻接顶点邻接顶点 若( 若( vvii,v,vjj)是一条无向边,则称)是一条无向边,则称顶点顶点 vvii和和 vvjj互为邻接点,或称互为邻接点,或称 vvii和和 vvjj相邻相邻接,并称边(接,并称边( vvii,v,vjj)关联于顶点)关联于顶点 vvii和和 vvjj,,或称(或称( vvii,v,vjj)与顶点)与顶点 vvii和和 vvjj相关联。 相关联。

Page 4: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

顶点的度顶点的度 一个顶点 一个顶点 vv 的度是与它相关联的边的的度是与它相关联的边的条数。记作条数。记作 TD(TD(vv)) 。。顶点 顶点 v v 的入度的入度 是以 是以 v v 为终点的有向边的条数为终点的有向边的条数 , , 记作 记作 ID(v)ID(v) 。。顶点 顶点 v v 的出度的出度是以 是以 v v 为始点的有向边的条数为始点的有向边的条数 , , 记记作 作 OD(v)OD(v) 。。子图子图 设有两个图 设有两个图 GG == (V, E) (V, E) 和 和 G’G’ == (V’, E’)(V’, E’) 。。若 若 V’V’ V V 且 且 E’E’E, E, 则称 图则称 图 G’ G’ 是 图是 图 G G 的子图。的子图。

Page 5: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

路径 路径 在图 在图 GG == ((VV, , EE)) 中中 , , 若存在一个顶点序若存在一个顶点序列列 vvpp11, , vvpp22, …, , …, vvpmpm ,使得,使得 ((vvii, , vvpp11)) 、、 ((vvpp11, , vvpp22)) 、、 ...... 、、 ((vvpmpm,, v vjj)) 均属于均属于 EE ,,则称顶点则称顶点 vvii 到到 vvjj 存在存在一一 条路径。若一条路径上除了条路径。若一条路径上除了 vvii 和和 vvjj 可以相同外可以相同外,, 其余顶点均不相同,则称此路径为一条其余顶点均不相同,则称此路径为一条简单路径简单路径 。起点和终点相同的路径称为。起点和终点相同的路径称为简单回路或简单环简单回路或简单环。。

Page 6: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

图的连通图的连通 在无向图 在无向图 GG 中,若两个顶点中,若两个顶点 vvii 和和 vvjj 之间之间有有 路径存在,则称路径存在,则称 vvii 和和 vvjj 是连通的。若是连通的。若 GG 中任意中任意两两 个顶点都是连通的,则称个顶点都是连通的,则称 GG 为为连通图连通图。。非连通图非连通图的的 极大连通子图叫做极大连通子图叫做连通分量连通分量。。 强连通图与强连通分量强连通图与强连通分量 在有向图中在有向图中 , , 若对于若对于每一对顶点每一对顶点 vvii 和和 vvjj, , 都存在一条从都存在一条从 vvii 到到 vvjj 和从和从 vvjj

到到 vvii 的路径的路径 ,, 则称此图是强连通图。非强连通图的则称此图是强连通图。非强连通图的极大强连通子图叫做强连通分量。极大强连通子图叫做强连通分量。

权权 某些图的边具有与它相关的数某些图的边具有与它相关的数 , , 称之为称之为权权。。这种带权图叫做这种带权图叫做网络网络。。

Page 7: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

1

2

3

5

6

8

74

A

B

D

C

E

10

7

9

6

6

7

12

315

16

60

30

45

35

80

40

75

权图权图

Page 8: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

生成树生成树 一个连通图的生成树是它的极 一个连通图的生成树是它的极小小 连通子图,在连通子图,在 nn 个顶点的情形下,有个顶点的情形下,有 nn--11 条条 边。边。生成林生成林 若 若 GG 是一个不连通的无向图,是一个不连通的无向图, GG的每个连通分量都有一棵生成树,这些生的每个连通分量都有一棵生成树,这些生成树构成成树构成 GG 的生成森林,简称生成林。 的生成森林,简称生成林。

Page 9: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.2 7.2 图的存储结构图的存储结构

在图的邻接矩阵表示中,有一个记录各个顶点信息在图的邻接矩阵表示中,有一个记录各个顶点信息的顶点表,还有一个表示各个顶点之间关系的邻接的顶点表,还有一个表示各个顶点之间关系的邻接矩阵。矩阵。

设图 设图 A = (V, E)A = (V, E) 是一个有 是一个有 n n 个顶点的图,则图的个顶点的图,则图的邻接矩阵是一个二维数组 邻接矩阵是一个二维数组 A .Edge[n][n]A .Edge[n][n] ,定义:,定义:

无向图的邻接矩阵是对称的,有向图的邻接矩阵一无向图的邻接矩阵是对称的,有向图的邻接矩阵一般情况下是不对称的。般情况下是不对称的。

邻接矩阵 邻接矩阵 (Adjacency Matrix)(Adjacency Matrix) 表示法表示法

,

),( , ,]][[

否则如果

0

><1A.Edge

EjiEjiji

或者

Page 10: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

在有向图中在有向图中 , , 统计第 统计第 ii 行 行 1 1 的个数可得顶点 的个数可得顶点 ii 的出的出度,统计第 度,统计第 j j 列 列 1 1 的个数可得顶点 的个数可得顶点 jj 的入度。的入度。

在无向图中在无向图中 , , 统计第 统计第 ii 行 行 (( 列列 ) 1 ) 1 的个数可得顶点的个数可得顶点 ii 的度。的度。

Page 11: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

网络的邻接矩阵网络的邻接矩阵

),( ,

,

,

),,(

]][[ .

ji

ji

EjiEjijijiW

jiEdge

==

=!

<!

0

=A

对角线但是,否则

或且如果

Page 12: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

邻接矩阵表示法中图的类型定义邻接矩阵表示法中图的类型定义::#define MAXSIZE 100 /* 图的顶点个数 */typedef int datatype;typedef struct { datatype vexs[MAXSIZE] ; /* 顶点信息表 */ int edges[MAXSIZE][ MAXSIZE] ;/ * 邻接矩阵 */ int n , e ; /* 顶点数和边数 */ }graph ;  

Page 13: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

2

1 4

3 5

5

4

3

2

1

vexs

0 1 1 1 1

1 0 0 1 0

1 0 0 0 1

1 1 0 0 0

1 0 1 0 0

edges

无向图无向图

Page 14: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

B

A D

C E

E

D

C

B

A

vexs

0 1 1 1 1

0 0 0 0 0

0 0 0 0 1

0 1 0 0 0

0 0 0 0 0

edges

有向图有向图

Page 15: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

2

1

5

3

4

6

5

4

3

2

1

vexs

20 30

20 40

30 50

40 50 70 80

70

80

edges

6

20

30 50

40

70

80

有向权图有向权图

Page 16: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

邻接矩阵表示法中无向网络的建立算法邻接矩阵表示法中无向网络的建立算法void Create_Graph ( graph *ga ){ int i , j , k , w ; printf (" 请输入图的顶点数和边数: \n") ; scanf ("%d" , &(ga->n) , & (ga->e)) ; printf (" 请输入顶点信息 ( 顶点编号 ) ,建立顶点信息表: \n") ; for(i = 0 ; i<ga->n ; i++) scanf("%c",&(ga->vexs[i])) ; /* 输入顶点信息 */ for (i = 0 ; i<ga->n ; i++) /* 邻接矩阵初始化 */ for (j = 0 ; j<ga->n ; j++) ga->edges[i][j] = 0 ; for (k = 0 ; k<ga->e ; k++) /* 读入边的顶点编号和权值,建立邻接矩阵 */ { printf (" 请输入第 %d 条边的顶点序号 i , j 和权值 w : ",k+1) ; scanf ("%d , %d , %d" , &i , &j , &w) ; ga->edges[i][j] = w ; ga->edges[j][i] = w ; } }

Page 17: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法分析算法分析

该算法的执行时间是该算法的执行时间是 O(n+nO(n+n22+e)+e) ,由于,由于 e< ne< n22

,所以算法的时间复杂度为,所以算法的时间复杂度为 O(nO(n22)) 。 。

Page 18: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

邻接表 邻接表 (Adjacency List)(Adjacency List) 表示法表示法无向图的无向图的邻接邻接表表

表结点表头结点data first vertex next

v1

v2 v3

v4

v1  

v2  

v3  

v4  

2

1

1

2

3 ^

4 ^

4 ^

3 ^

data first vertex next

G5

Page 19: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

每个链表的上边附设一个每个链表的上边附设一个表头结点表头结点,在表头,在表头结点中,除了设有链域结点中,除了设有链域 firstfirst 用于指向链表中用于指向链表中第一个结点之外,还设有存储顶点第一个结点之外,还设有存储顶点 vvii 名或其名或其它有关信息的数据域它有关信息的数据域 data data 。。把同一个顶点发出的边链接在同一个边链表把同一个顶点发出的边链接在同一个边链表中,链表的每一个结点代表一条边,叫做中,链表的每一个结点代表一条边,叫做表表结点结点,结点中保存有与该边相关联的另一顶,结点中保存有与该边相关联的另一顶点的顶点下标 点的顶点下标 vertexvertex 和指向同一链表中下一和指向同一链表中下一个表结点的指针 个表结点的指针 nextnext 。。

Page 20: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

有向图的邻接表和逆邻接表有向图的邻接表和逆邻接表

在有向图的邻接表中,第 在有向图的邻接表中,第 ii 个边链表链接的个边链表链接的边都是顶点 边都是顶点 ii 发出的边。也叫做发出的边。也叫做出边表出边表。。

在有向图的逆邻接表中,第 在有向图的逆邻接表中,第 ii 个边链表链接个边链表链接的边都是进入顶点 的边都是进入顶点 ii 的边。也叫做的边。也叫做入边表入边表。。

2 ^

1 3 ^

V1

V2

V3

2 ^

1 ^

2 ^

V1

V2

V3

G6

邻接表 逆邻接表v

1

v

2v

3

Page 21: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

邻接表的类型定义邻接表的类型定义#define nmax 100 /* 假设顶点的最大数为 100*/typedef struct node *pointer ;struct node { /* 表结点类型 */

int vertex ;struct node *next ;} nnode ;

typedef struct {/* 表头结点类型,即顶点表结点类型 */datatype data ;pointer first ; /* 边表头指针 */}headtype ;

typedef struct { /* 表头结点向量,即顶点表 */headtype adlist[nmax];int n , e ;}lkgraph ;

Page 22: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

建立无向图邻接表的算法建立无向图邻接表的算法 void creatqraph ( Ikgraph *ga ){ /* 建立无向图的邻接表 */int i , j , e , k ;pointer p ;printf(“ 请输入顶点数: \n”) ;scanf (“%d” , &(ga->n)) ;for (i =1; i<= ga->n; i++) { /* 读入顶点信息,建立顶点表 */

scanf (“ \n %c” , &( ga->adlist [ i ]. data) ) ;ga->adlist [ i ]. first = NULL ;}

e = 0 ;scanf (“\n%d,%d\n” , &i , &j ) ; /* 读入一个顶点对号 i 和 j*/while (i>0) { /* 读入顶点对号 , 建立边表 */e++ ; /* 累计边数 */p = (pointer)malloc(size(struct node)) ; /* 生成新的邻接点序号为 j 的表结点 */p-> vertex = j;p->next = ga->adlist[i] . first ;ga->adlist[i].first = p ; /* 将新表结点插入到顶点 vi 的边表的头部 */

p = (pointer)malloc(size(struct node)) ; /* 生成邻接点序号为 i 的表结点 */p-> vertex = i ;p->next = ga->adlist[j].first;ga->adlist[j].first=p ; /* 将新表结点插入到顶点 vj 的边表头部 */

scanf (“\n%d,%d\n” , &i , &j ) ; /* 读入一个顶点对号 i 和 j*/}ga->e = e ;}

Page 23: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

该算法的时间复杂度是该算法的时间复杂度是 OO (( nn ++ ee ) ) 在邻接表的边链表中,各个表结点的链入顺序在邻接表的边链表中,各个表结点的链入顺序任意,视表结点输入次序而定。任意,视表结点输入次序而定。

设图中有 设图中有 n n 个顶点,个顶点, e e 条边,则用邻接表表条边,则用邻接表表示无向图时,需要 示无向图时,需要 n n 个表头结点,个表头结点, 22e e 个表个表结点;用邻接表表示有向图时,若不考虑逆邻结点;用邻接表表示有向图时,若不考虑逆邻接表,只需 接表,只需 n n 个表头结点,个表头结点, e e 个表结点。个表结点。

带权图的边结点中还应保存该边上的权值 带权图的边结点中还应保存该边上的权值 costcost 。。

Page 24: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

网络 网络 (( 带权图带权图 ) ) 的邻接表的邻接表表结点

vertex cost next

Page 25: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.3 7.3 图的遍历图的遍历 从已给的连通图中某一顶点出发,沿着一些边访遍从已给的连通图中某一顶点出发,沿着一些边访遍

图中所有的顶点,且使每个顶点仅被访问一次,就图中所有的顶点,且使每个顶点仅被访问一次,就叫做叫做图的遍历图的遍历 ( Graph Traversal )( Graph Traversal ) 。。

图中可能存在回路,且图的任一顶点都可能与其它图中可能存在回路,且图的任一顶点都可能与其它顶点相通,在访问完某个顶点之后可能会沿着某些顶点相通,在访问完某个顶点之后可能会沿着某些边又回到了曾经访问过的顶点。边又回到了曾经访问过的顶点。

为了避免重复访问,可设置一个标志顶点是否被访为了避免重复访问,可设置一个标志顶点是否被访问过的辅助数组 问过的辅助数组 visited [ ]visited [ ] ,它的初始状态为 ,它的初始状态为 00 ,,在图的遍历过程中,一旦某一个顶点 在图的遍历过程中,一旦某一个顶点 i i 被访问,就被访问,就立即让 立即让 visited [i] visited [i] 为 为 11 ,防止它被多次访问。,防止它被多次访问。

Page 26: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

深度优先搜索深度优先搜索 DFS DFS ( Depth First Search )( Depth First Search )

深度优先搜索的示例深度优先搜索的示例

Page 27: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

DFS DFS 在访问图中某一起始顶点 在访问图中某一起始顶点 v v 后,由 后,由 v v 出出发,发,

访问它的任一邻接顶点 访问它的任一邻接顶点 v v 11 ;再从 ;再从 v v 1 1 出发,访问与出发,访问与 v v 11 邻接但还没有访问过的顶点 邻接但还没有访问过的顶点 v v 22 ;然后再从 ;然后再从 v v 2 2 出出发,进行类似的访问,… 如此进行下去,直至到达发,进行类似的访问,… 如此进行下去,直至到达所有的邻接顶点都被访问过的顶点所有的邻接顶点都被访问过的顶点 vvkk 为止。接着,为止。接着,退回一步,退到前一次刚访问过的顶点,看是否还退回一步,退到前一次刚访问过的顶点,看是否还有其它没有被访问的邻接顶点。如果有,则访问此有其它没有被访问的邻接顶点。如果有,则访问此顶点,之后再从此顶点出发,进行与前述类似的访顶点,之后再从此顶点出发,进行与前述类似的访问;如果没有,就再退回一步进行搜索。重复上述问;如果没有,就再退回一步进行搜索。重复上述过程,直到连通图中所有顶点都被访问过为止。过程,直到连通图中所有顶点都被访问过为止。

Page 28: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

深度优先搜索算法深度优先搜索算法void DFS ( graph *g ) /* 按深度优先搜索法遍历图 g*/ { int i ; for ( i = 0 ; i < g->n ; i++ ) visid[i] = 0 ; /* 初始化数组 visid ,使每个元素为 0*/ /* 标示图中的每个结点都未曾访问过 */ for ( i= 0 ; i<g->n ; i++ ) if ( !visid[i] ) DFSM(g , i) ; /* 调用函数 DFSM ,对图进行遍历 */ }void DFSM ( graph *g , int i ) /* 邻接矩阵上进行 DFS 遍历 */ { int j ; printf ( " 深度优先遍历结点: %c\n" , g->vexs[i] ); visid[i]=1; /* 假定 g->vexs[i] 为顶点的编号,然后变访问标志为 1*/ for ( j =0 ; j<g->n ; j++ ) if (( g->edges[i][j] = =1 )&&! visid[j] ) DFSM ( g , j ); }

Page 29: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

void DFSL ( lkgraph *g , int n ) /* 邻接表上进行 DFS 遍历 */ { pointer p; int j ; printf ( " %d\n" , g-> adlist[n].data ); /* 访问出发点,输出顶点数据 */ visid[i]=1; /* 然后变访问标志为 1*/ for ( p = g->adlist[n].first ; p!=NULL ; p = p->next ) if (! visid[p-> vertex ] ) DFSL ( g , p-> vertex ); }

Page 30: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法分析算法分析 图中有 图中有 n n 个顶点,个顶点, e e 条边。条边。 如果用邻接表表示图,沿链可以找到某个顶点 如果用邻接表表示图,沿链可以找到某个顶点

v v 的所有邻接顶点 的所有邻接顶点 vvii 。由于总共有 。由于总共有 22e e 个边个边结点,所以扫描边的时间为结点,所以扫描边的时间为 O(O(ee)) 。而且对所。而且对所有顶点递归访问有顶点递归访问 11 次,所以遍历图的时间复杂次,所以遍历图的时间复杂性为性为 O(O(nn++ee)) 。。

如果用邻接矩阵表示图,则查找每一个顶点的如果用邻接矩阵表示图,则查找每一个顶点的所有邻接点,所需时间为所有邻接点,所需时间为 O(O(nn)) ,则遍历图中,则遍历图中所有的顶点所需的时间为所有的顶点所需的时间为 O(O(nn22)) 。。

Page 31: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

广度优先搜索广度优先搜索 BFS BFS ( Breadth First Search( Breadth First Search ))广度优先搜索的示例广度优先搜索的示例

广度优先搜索过程广度优先搜索过程 广度优先生成树 广度优先生成树

Page 32: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

使用广度优先搜索在访问了起始顶点 使用广度优先搜索在访问了起始顶点 v v 之后,由 之后,由 v v 出发,依次访问 出发,依次访问 v v 的各个未曾被访问过的邻接顶点 的各个未曾被访问过的邻接顶点 v v 11, , v v 22, …, , …, v v tt ,然后再顺序访问 ,然后再顺序访问 v v 11, , v v 22, …, , …, v v tt 的所有的所有还未被访问过的邻接顶点。再从这些访问过的顶点还未被访问过的邻接顶点。再从这些访问过的顶点出发,再访问它们的所有还未被访问过的邻接顶点,出发,再访问它们的所有还未被访问过的邻接顶点,… 如此做下去,直到图中所有顶点都被访问到为止。… 如此做下去,直到图中所有顶点都被访问到为止。

广度优先搜索是一种分层的搜索过程,每向前走一广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有往步可能访问一批顶点,不像深度优先搜索那样有往回退的情况。因此,广度优先搜索不是一个递归的回退的情况。因此,广度优先搜索不是一个递归的过程,其算法也不是递归的。过程,其算法也不是递归的。

Page 33: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

为了实现逐层访问,算法中使用了一个队列,为了实现逐层访问,算法中使用了一个队列,以记忆正在访问的这一层和上一层的顶点,以以记忆正在访问的这一层和上一层的顶点,以便于向下一层访问。便于向下一层访问。

与深度优先搜索过程一样,为避免重复访问,与深度优先搜索过程一样,为避免重复访问,需要一个辅助数组 需要一个辅助数组 visitedvisited [ ] [ ] ,以便给被访问,以便给被访问过的顶点加标记。过的顶点加标记。

Page 34: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

广度优先搜索算法广度优先搜索算法void BFS ( graph *g , int v ) /*v 是出发顶点的序号,按广度优先搜索法遍历图 g ,采用邻接矩阵存储结构, BFS 遍历 */{ int j , n ;seqqueue q ; /* 假设采用顺序队列,定义顺序队列类型变量 q*/n = g->n ;init_seqqueue (& q ); /* 队列初始化 */printf(“ 访问出发点 %d”,v) ; /* 访问出发点,假设为输出顶点序号 */visid[v] = 1 ; /* 置访问标志为 1 ,表示此点已访问过 */en_sqqueue (& q , v ); /* 顶点 v 入队 */while (!empty_Seqqueue( & q)){ /* 队列空否? */

de_ sqqueue (& q ,& v ); /* 队列非空时,出队 */for ( j= l ; j<= n ; j++ )if ( q->adges [v][j] = = l &&! visid[j] ){ printf(“ 访问顶点 %d” , j) ; visid[j] = 1 ; /* 置顶点 j 被访问

标志 */en_seqqueue( & q , j) ; /* 顶点 j 入队 */}

}}

Page 35: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

void BFSL ( graph *g , int v ){ /* 采用邻接表存储结构, BFS 遍历 */seqqueue q ; /* 假设采用顺序队列 , 定义顺序队列类型变量 q*/pointer p ; init_seqqueue (& q ); /* 队列初始化 */printf (“访问出发点 %d”,v ); /* 访问出发点,假设为输出顶点序号 */visid[v] = 1 ; /* 置访问标志为 1 ,表示此点已访问过 */en_seqqueue (& q , v ); /* 顶点 v 入队 */while (! empty _seqqueue (& q )){ de_seqqueue (& q ,& v );p = g->adlist[v] . first ;While ( p != NULL ){ if ( ! visid[p->vertex] ){ printf("%d” , p-> vertex) ;visid[p-> vertex ]= 1 ;en_ seqqueue ( & q , p-> vertex) ;}p = p->next ;}}}

Page 36: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法分析算法分析 当使用邻接矩阵表示图时,因为每个顶点当使用邻接矩阵表示图时,因为每个顶点均入队、出队一次,所以算法均入队、出队一次,所以算法 BFSBFS 的外的外循环次数为循环次数为 nn ,且其内循环也是,且其内循环也是 nn 次,因次,因此算法此算法 BFSBFS 的时间复杂度为的时间复杂度为 O(O(nn22)) 。。

当使用邻接表表示图时,同样知道函数当使用邻接表表示图时,同样知道函数 BBFSLFSL 的外循环次数也是的外循环次数也是 nn ,且其内循环次,且其内循环次数取决于各顶点的边表结点个数,而内循数取决于各顶点的边表结点个数,而内循环执行的总次数是边表结点的总个数环执行的总次数是边表结点的总个数 2e2e ,,因此执行函数因此执行函数 BFSLBFSL 的时间复杂度是的时间复杂度是 O(O(nn+e+e)) 。。

Page 37: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

非连通图的遍历非连通图的遍历非连通图的遍历必须多次调用深度优先搜索或广度非连通图的遍历必须多次调用深度优先搜索或广度优先搜索算法,以优先搜索算法,以 DFSDFS为例:为例:TRAVER() /* 遍历用邻接矩阵表示的非连通图 */{ int i; for ( i = 0; i < n; i++) visited[i] = FALSE; /* 标志数组初始化 */ for ( i = 0; i < n; i++) { if ( !visited[i]) DFS(i); /* 从顶点出发遍历一个连 通分量 */ printf( “comp end\n”); }}

Page 38: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.4 7.4 生成树生成树 连通图连通图 GG 的一个子图如果是一棵包含的一个子图如果是一棵包含 GG 的的所有顶点的树,则该子图称为所有顶点的树,则该子图称为 GG 的的生成树生成树。。 生成树是连通图的极小连通子图。所谓极生成树是连通图的极小连通子图。所谓极小是指:若在树中任意增加一条边,则将出小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成现一个回路;若去掉一条边,将会使之变成非连通图。非连通图。 生成树各边的权值总和称为生成树的权。生成树各边的权值总和称为生成树的权。权最小的生成树称为权最小的生成树称为最小生成树。最小生成树。

Page 39: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

用不同的遍历图的方法,可以得到用不同的遍历图的方法,可以得到不同的生成树;从不同的顶点出发,也可能得不同的生成树;从不同的顶点出发,也可能得到不同的生成树。到不同的生成树。 按照生成树的定义,按照生成树的定义, n n 个顶点的连通个顶点的连通网络的生成树有 网络的生成树有 n n 个顶点、个顶点、 nn--1 1 条边。条边。 构造最小生成树,要解决以下两个问题构造最小生成树,要解决以下两个问题::1.1. 尽可能选取权值小的边,但不能构成回路尽可能选取权值小的边,但不能构成回路。。2.2. 选取选取 n-1n-1 条恰当的边以连接网的 条恰当的边以连接网的 nn 个顶点个顶点。。

Page 40: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

构造最小生成树的普里姆构造最小生成树的普里姆 (Prim)(Prim) 算法算法普里姆算法的基本思想:普里姆算法的基本思想: 从连通网络 从连通网络 NN = { = { VV, , EE } } 中的某一顶点 中的某一顶点 uu0 0

出出发,选择与它关联的具有最小权值的边发,选择与它关联的具有最小权值的边 ((uu00, , vv)) ,,将其顶点加入到生成树的顶点集合将其顶点加入到生成树的顶点集合 UU 中。以后每中。以后每一步从一个顶点在一步从一个顶点在 UU 中,而另一个顶点不在中,而另一个顶点不在 UU 中中的各条边中选择权值最小的边的各条边中选择权值最小的边 ((uu, , vv),), 把它的顶点把它的顶点加入到集合加入到集合 UU 中。如此继续下去,直到网络中的中。如此继续下去,直到网络中的所有顶点都加入到生成树顶点集合所有顶点都加入到生成树顶点集合 UU 中为止。中为止。

Page 41: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

用普里姆算法构造最小生成树的过程用普里姆算法构造最小生成树的过程

1

2

3

4

65

56

51

7

3 2

5 4

6

1

2

3

4

65

51

3 2

4

Page 42: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

PrimPrim 算法的形式描述如下:算法的形式描述如下: 置置 TT 为任意一个顶点;为任意一个顶点;

求初始候选边集;求初始候选边集;

whilewhile (( TT 中结点数<中结点数< nn ))

{从候选边集中选取最短边({从候选边集中选取最短边( uu ,, vv ););

将(将( uu ,, vv )及顶点)及顶点 vv ,扩充到,扩充到 TT 中;中;

调整候选边集;调整候选边集;

}}

Page 43: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

PRIM算法void prim ( graph *g , int u ) { int v , k , j , min ;for (v =1 ; v<=g->n ; v++) if ( v ! = u ){ minedge[v].end=u ; minedge[v].len = g->edges[v][u] ;}minedge[u].len = 0 ;for ( k = 1 ; k< g->n ; k++ ) { min = minedge[k].len ;v = k ;for ( j =1 ; j<g->n ; j++ )if ( minedge[j].len>0&&minedge[j].len<min )

{ min = minedge[j].len ;v = j ;}if ( min=INTMAX ){   printf ( “ 图不连通,无生成树!” ) ;return(0) ;}     printf(“%d %d”,v, minedge[v].end) ;minedge[v].len = -minedge[v].len ;for(j = 1 ; j<=g->n ; j++)if ( g-> edges [j][v]<minedge[j].len ){ minedge[j].len = g-> edges [j][v] ;minedge[j].end = v ;}}}

Page 44: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

2 2

1

2

0

22

0

)1(2))1()1(n

kj

n

kj

n

k

n

kj

n

k

OOO(

算法分析算法分析

上述算法的初始化时间是上述算法的初始化时间是 O(1)O(1) ,, kk 循循环中有两个循环语句,其时间大致为:环中有两个循环语句,其时间大致为:

令令 O(1)O(1) 为某一正常数为某一正常数 CC ,展开上述求,展开上述求和公式可知其数量级仍是 和公式可知其数量级仍是 n n 的平方。所以的平方。所以,整个算法的时间复杂性是,整个算法的时间复杂性是 O(nO(n22)) 。。

Page 45: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

构造最小生成树的克鲁斯卡尔 构造最小生成树的克鲁斯卡尔 (Kruskal) (Kruskal) 算法算法克鲁斯卡尔算法的基本思想:克鲁斯卡尔算法的基本思想: 设有一个有 设有一个有 n n 个顶点的连通网络 个顶点的连通网络 NN = { = {

VV, , EE }, },

最初先构造一个只有 最初先构造一个只有 n n 个顶点,没有边的非连通个顶点,没有边的非连通图 图 TT = { = { VV, , }, }, 图中每个顶点自成一个连通分量。图中每个顶点自成一个连通分量。当在 当在 E E 中选到一条具有最小权值的边时中选到一条具有最小权值的边时 ,, 若该边的若该边的两个顶点落在不同的连通分量上,则将此边加入两个顶点落在不同的连通分量上,则将此边加入到 到 T T 中;否则将此边舍去,重新选择一条权值最中;否则将此边舍去,重新选择一条权值最小的边。小的边。 如此重复下去,直到所有顶点在同一个连通如此重复下去,直到所有顶点在同一个连通分量上为止。分量上为止。

Page 46: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

用克鲁斯卡尔算法构造最小生成树的过程用克鲁斯卡尔算法构造最小生成树的过程

1

2

3

4

65

56

51

7

3 2

5 4

6

1

2

3

4

65

51

3 2

4

Page 47: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

克鲁斯卡尔算法的形式描述如下:克鲁斯卡尔算法的形式描述如下:T = (V, φ)T = (V, φ) ;;While ( TWhile ( T 中所含边数 中所含边数 < n-1 )< n-1 ) { { 从从 EE 中选取当前最短边 中选取当前最短边 (u,v);(u,v); 从从 EE 中删除边中删除边 (u,v);(u,v); if ((u,v) if ((u,v) 并入并入 TT 之后不产生回路 之后不产生回路 )) 将边 将边 (u,v) (u,v) 并入并入 TT 中;中; }}

Page 48: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

typedef Struct { int v1 , v2 ; int len ; }edgetype ; /* 边的类型:两个端点号和边长 */int parent[nmax+1] ; /* 结点双亲的指针数组,设为全局量, nmax 为结点数最大值*/int getroot ( int v ) /* 找结点 v 所在的树根 */{ int i ; i = v ;while ( parent[i]>0 ) i = parent[i] ;return i ; /* 若无双亲(初始点),双亲运算结果为其自己 */ }int getedge ( edgetype em [ ] , int e ) /* 找最短边, e 为边数 */{ int i, j, min = 0;for (i =1 ; i<= e ; i++)if (em [i-1].len<min) min = em [i-1].len ;return min ;}

KruskalKruskal 算算法 法

Page 49: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

void kruskal ( edqetype em [ ] , int n , int e ) /*n 为结点数, e 为边数 */{ int i , p1 , p2 , m , i0 ;for ( i=1 ; i<= n ; i++ ) /* 初始结点为根,无双亲 */parent[i ] = -1 ; /* 以后用于累计结点个数,此初值不能置为 0*/m = 1 ;while ( m< n ){ i0 = getedge ( em , e ); /* 获得最短边号 */p1= getroot ( em [i0].v1 );p2= getroot ( em [i0].v2 ) ;if ( p1= = p2 ) continue ; /* 连通分量相同,不合并 */if ( p1>p2 ){parent [ p2 ] = parent [ p1 ]+ parent [ p2 ]; /*p2 的双亲中累计结点总数(为负值) */parent[pl]= p2 ; /*p1 成为 p2 的孩子}else {parent[ p1]= parent[p1] + parent[p2];parent[p2]= p1;}m++ ;printf (“%d%d%d\n” , m , em[i0].v1 , em [i0].v2 ) ;}}

Page 50: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法分析算法分析

用用 KruskalKruskal 算法构造最小生成树算法构造最小生成树的时间复杂度为的时间复杂度为 OO (( elogeeloge ),与网中边),与网中边的数目的数目 ee 有关,因此,它适用于求稀疏图有关,因此,它适用于求稀疏图的最小生成树。的最小生成树。

Page 51: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.5 7.5 最短路径最短路径 最短路径问题:最短路径问题:如果从图中某一顶点如果从图中某一顶点 (( 称称为源点为源点 )) 到达另一顶点到达另一顶点 (( 称为终点称为终点 )) 的路径的路径可能不止一条,如何找到一条路径,使得可能不止一条,如何找到一条路径,使得沿此路径各边上的权值总和达到最小。沿此路径各边上的权值总和达到最小。

问题解法:问题解法: 单源最短路径 — 单源最短路径 — DijkstraDijkstra 算法算法 任意顶点对之间的最短路径 任意顶点对之间的最短路径 — — FloydFloyd 算法算法

Page 52: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

单源最短路径问题单源最短路径问题 问题的提出: 给定一个带权有向图问题的提出: 给定一个带权有向图 GG 与源点与源点 vv ,,求从求从 vv 到到 GG 中其它顶点的最短路径。限定各边中其它顶点的最短路径。限定各边上的权值大于或等于上的权值大于或等于 00 。。

为求得这些最短路径,为求得这些最短路径, DijkstraDijkstra 提出按路径长度提出按路径长度的递增次序,逐步产生最短路径的算法。首先求的递增次序,逐步产生最短路径的算法。首先求出长度最短的一条最短路径,再参照它求出长度出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从顶点次短的一条最短路径,依次类推,直到从顶点 vv

到其它各顶点的最短路径全部求出为止。到其它各顶点的最短路径全部求出为止。 举例说明举例说明

Page 53: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

DijkstraDijkstra 逐步求解的过程逐步求解的过程

源点 终点 最 短 路 径 路径长度 v0 v1 (v0, v1) 10

v2 — (v0, v1, v2) (v0, v3, v2) — 60 50 v3 (v0, v3) 30 v4 (v0, v4) (v0, v3, v4) (v0, v3, v2, v4) 100 90 60

Page 54: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

2 5

3 4

10

30

10

20

Page 55: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

DijkstraDijkstra 算法算法void dijkstra ( graph *G , int v , int *dist,int *path,char *S ){int i,j,k ,pre , min ;for ( i=l;i<=G->n;i++ ) { s[i]= 0 ;dist[i] = G->adges[v][i] ;path[i] = V ;}s[v] = 1 ;dist[v] = 0 ; for ( i = 1 ; i<G->n ; i++ ){min=INTMAX ;for(j=1;j<= G->n ; j++)if ( ! s[j]&&dist[j]< min ){ min = dist[j];k=j; }

if(min = = INTMAX) break; S[k]=1 ;for ( j = l ; j<=G->n ; j++ )if (! s[j]&&dist[j]>dist[k]+ G->adges[k][j]){dist[j]=dist[k]+G->adges[k][j] ;Path[j]=k;}} for ( i=1 ; i<= G->n ; i++ ) { printf(“ 顶点 %d 的最短路径为: %d\n” , i , dist [ i ] ) ;Pre = path[i] ;do { printf(“<- %d”, pre) ;pre = path[pre] ;} while ( pre ! = v );if(dist[i]= =INTMAX)printf(” 无 路 径 \n”);}}

Page 56: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法说明算法说明

对于顶点对于顶点 ii 和和 jj ::

11 、首先,考虑从、首先,考虑从 ii 到到 jj 是否有以顶点是否有以顶点 11 为为中间点的路径,:中间点的路径,: ii ,, 11 ,, jj ,即考虑图中,即考虑图中是否有边是否有边 <i,1><i,1> 和和 <1,j><1,j> ,若有,则新路径,若有,则新路径ii ,, 11 ,, jj 的长度是的长度是 C[i][1]+C[1][j]C[i][1]+C[1][j] ,比,比较路径较路径 ii ,, jj 和和 ii ,, 11 ,, jj ,的长度,并以,的长度,并以较短者为当前所求得的最短路径,。该路径较短者为当前所求得的最短路径,。该路径是中间点序号不大于是中间点序号不大于 11 的最短路径。的最短路径。

所有顶点对之间的最短路径所有顶点对之间的最短路径

Page 57: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

22 、其次,考虑从、其次,考虑从 ii 到到 jj 是否包含顶点是否包含顶点 22 为中为中间点的路径:间点的路径: ii ,, ...... ,, 22 ,, ...... ,, jj ,若没,若没有,则从有,则从 ii 到到 jj 的最短路径仍然是第一步中求的最短路径仍然是第一步中求出的,即从出的,即从 ii 到到 jj 的中间点序号不大于的中间点序号不大于 11 的最的最短路径;若有,则短路径;若有,则 ii ,, ...... ,, 22 ,, ...... ,, jj 可可分解成两条路径分解成两条路径 ii ,, ...... ,, 22 和和 22 ,, ...... ,, jj,而这两条路径是前一次找到的中间点序号,而这两条路径是前一次找到的中间点序号不大于不大于 11 的最短路径,将这两条路径相加就的最短路径,将这两条路径相加就得到路径得到路径 ii ,, ...... ,, 22 ,, ...... ,, jj 的长度,将的长度,将该长度与前一次求出的从该长度与前一次求出的从 ii 到到 jj 的中间点序号的中间点序号不大于不大于 11 的最短路径长度比较,取其较短者的最短路径长度比较,取其较短者作为当前求得的从作为当前求得的从 ii 到到 jj 的中间点序号不大于的中间点序号不大于22 的最短路径。的最短路径。

Page 58: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

33、然后,再选择顶点、然后,再选择顶点 33 加入当前求得的从加入当前求得的从 ii 到到 jj中间点序号不大于中间点序号不大于 22 的最短路径中,按上述步骤的最短路径中,按上述步骤进行比较,从未加入顶点进行比较,从未加入顶点 33作中间点的最短路径作中间点的最短路径和加入顶点和加入顶点 33作中间点的新路径中选取较小者,作中间点的新路径中选取较小者,作为当前求得的从作为当前求得的从 ii 到到 jj 的中间点序号不大于的中间点序号不大于 33的最短路径。依次类推,直到考虑了顶点的最短路径。依次类推,直到考虑了顶点 nn加入加入当前从当前从 ii 到到 jj 的最短路径后,选出从的最短路径后,选出从 ii 到到 jj 的中的中间点序号不大于间点序号不大于 nn 的最短路径为止。由于图中顶的最短路径为止。由于图中顶点序号不大于点序号不大于 nn ,所以从,所以从 ii 到到 jj 的中间点序号不的中间点序号不大于大于 nn 的最短路径,已考虑了所有顶点作为中间的最短路径,已考虑了所有顶点作为中间点的可能性。因而它必是从点的可能性。因而它必是从 ii 到到 jj 的最短路径。的最短路径。

Page 59: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法的基本思想就是:算法的基本思想就是:

从初始的邻接矩阵从初始的邻接矩阵 AA00开始,递推地生成矩阵序列开始,递推地生成矩阵序列 AA11,, AA22 ,, ...... ,, AAnn

]][[]][[0 jiCjiA

]}][[]][[],][[min{]][[1 jkAkiAjiAjiA kkkk

显然,显然, AA中记录了所有顶点对之间的最短路径长度。中记录了所有顶点对之间的最短路径长度。若要求得到最短路径本身,还必须设置一个路径矩阵若要求得到最短路径本身,还必须设置一个路径矩阵P[n][n]P[n][n],在第,在第 kk次迭代中求得的次迭代中求得的 path[i][j]path[i][j],是,是从从 ii 到到 jj 的中间点序号不大于的中间点序号不大于 kk的最短路径上顶点的最短路径上顶点 ii的后继顶点。算法结束时,由的后继顶点。算法结束时,由 path[i][j]path[i][j]的值就可的值就可以得到从以得到从 ii 到到 jj 的最短路径上的各个顶点。的最短路径上的各个顶点。

Page 60: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

FloydFloyd 算法算法void floyd ( graph *G ){ int i , j , k , next ; for ( i=1 ; i<=G->n ; i++) for ( j = 1 ; j<=G->n ; j++ ){ A[i][j] = G->adges[i][j] ; path[i][j]= 0 ; } for ( k=1;k<=G->n;k++ ) for( i=1 ; i<=G->n ; i++) for ( j= l ; j< = G->n ; j++ ) if ( A[i][k] + A[k][j]< A[i][j] ){ A[i][j] = A[i][k] + A[k][j] ; path[i][j]= k ; }

for ( i = 1 ; i<=G->n ; i++ ) for ( j= 1 ; j<=G->n ; j++ ){ printf (” 顶点 %d 路径长度 %d” , i, A[i][j] ); next = path[i][j] ; printf(“ 路径为: %d” , i ) ;do { printf (” -> %d” , next ); next=path[next][j] ; } while ( next! = j );if (A[i][j] = = INTMAX)

printf (“无路径 \n” ); }}

Page 61: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

2 5

3 4

50 10

30

10

20

60

100

Page 62: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

0

60020

100

500

10030100

A0

0

60020

100

500

1003060100

A2

0

30020

100

60500

1003060100

A3

0

30020

100

60500

1003050100

A4

Page 63: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

7.6 7.6 拓扑排序拓扑排序 计划、施工过程、生产流程、程序流程等都是计划、施工过程、生产流程、程序流程等都是““工程工程”。除了很小的工程外,一般都把工程”。除了很小的工程外,一般都把工程分为若干个叫做“分为若干个叫做“活动活动”的子工程。完成了这”的子工程。完成了这些活动,这个工程就可以完成了。些活动,这个工程就可以完成了。

例如,计算机专业学生的学习就是一个工程,例如,计算机专业学生的学习就是一个工程,每一门课程的学习就是整个工程的一些活动。每一门课程的学习就是整个工程的一些活动。其中一些课程要求有先修课程,另一些则不要其中一些课程要求有先修课程,另一些则不要求。这样在有的课程之间有先后关系,有的课求。这样在有的课程之间有先后关系,有的课程可以并行地学习。程可以并行地学习。

Page 64: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

CC11 高等数学高等数学 CC22 程序设计基础程序设计基础 CC33 离散数学 离散数学 CC11, C, C22

CC44 数据结构 数据结构 CC33, C, C22

CC55 高级语言程序设计 高级语言程序设计 CC22

CC66 编译方法 编译方法 CC55, C, C44

CC77 操作系统 操作系统 CC44, C, C99

CC88 普通物理 普通物理 CC11

CC99 计算机原理 计算机原理 CC88

Page 65: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

学生课程学习工程图

Page 66: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

可以用有向图表示一个工程。在这种有向可以用有向图表示一个工程。在这种有向 图中,图中,用顶点表示活动用顶点表示活动,,用有向边用有向边 <<VVii, , VV

jj>>

表示活动的前后次序。表示活动的前后次序。 VVi i 必须先于活动必须先于活动VVjj

进行。这种有向图叫做顶点表示活动的进行。这种有向图叫做顶点表示活动的 AOVAOV 网络网络 (Activity On Vertices)(Activity On Vertices) 。。 在在 AOVAOV 网络中,如果活动网络中,如果活动 VVi i 必须在活动必须在活动VVjj

之前进行,则存在有向边之前进行,则存在有向边 <<VVii, , VVjj>> , , AA

OVOV 网络中不能出现有向回路,即有向环。网络中不能出现有向回路,即有向环。在在 AOVAOV 网络中如果出现了有向环,则意味着网络中如果出现了有向环,则意味着 某项活动应以自己作为先决条件。某项活动应以自己作为先决条件。 因此,对给定的因此,对给定的 AOVAOV 网络,必须先判断网络,必须先判断它它 是否存在有向环。是否存在有向环。

Page 67: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

检测有向环的一种方法是对检测有向环的一种方法是对 AOVAOV 网络构造它的网络构造它的拓拓 扑有序序列。即将各个顶点 扑有序序列。即将各个顶点 (( 代表各个活动代表各个活动 ) ) 排列排列 成一个线性有序的序列,使得成一个线性有序的序列,使得 AOVAOV 网络中所有网络中所有应应 存在的前驱和后继关系都能得到满足。存在的前驱和后继关系都能得到满足。 这种构造这种构造 AOVAOV 网络全部顶点的拓扑有序序列的网络全部顶点的拓扑有序序列的运运 算就叫做算就叫做拓扑排序拓扑排序。。 如果通过拓扑排序能将如果通过拓扑排序能将 AOVAOV 网络的所有顶点都网络的所有顶点都排排 入一个拓扑有序的序列中,则该入一个拓扑有序的序列中,则该 AOVAOV 网络中必网络中必定定 不会出现有向环;相反,如果得不到满足要求不会出现有向环;相反,如果得不到满足要求的的 拓扑有序序列,则说明拓扑有序序列,则说明 AOVAOV 网络中存在有向环网络中存在有向环,, 此此 AOVAOV 网络所代表的工程是不可行的。网络所代表的工程是不可行的。

Page 68: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

例如,对学生选课工程图进行拓扑排序,得到的拓扑例如,对学生选课工程图进行拓扑排序,得到的拓扑有序序列为有序序列为

CC11 , C , C22 , C , C33 , C , C44 , C , C55 , C , C66 , C , C88 , C , C99 , C , C77

或 或 CC11 , C , C88 , C , C99 , C , C2 2 , C, C55 , C , C3 3 , C, C44 , C , C77 , C , C66

Page 69: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

进行拓扑排序的方法进行拓扑排序的方法输入输入 AOVAOV 网络。令 网络。令 n n 为顶点个数。为顶点个数。11 、在、在 AOVAOV 网络中选一个没有直接前驱的网络中选一个没有直接前驱的顶顶 点(即此顶点入度为点(即此顶点入度为 00 )) , , 并输出之并输出之;;22 、从图中删去该顶点、从图中删去该顶点 , , 同时删去所有它发同时删去所有它发出出 的有向边的有向边 ;;重复以上两步重复以上两步 , , 直到直到

全部顶点均已输出,拓扑有序序列形成,全部顶点均已输出,拓扑有序序列形成, 拓扑排序完成;或拓扑排序完成;或 图中还有未输出的顶点,但已跳出处理循图中还有未输出的顶点,但已跳出处理循 环。这说明图中还剩下一些顶点,它们环。这说明图中还剩下一些顶点,它们都都 有直接前驱,再也找不到没有前驱的顶有直接前驱,再也找不到没有前驱的顶点点 了。这时了。这时 AOVAOV 网络中必定存在有向环。网络中必定存在有向环。

Page 70: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

a b c d e f

Page 71: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

为了便于考察每个顶点的入度,在顶点表中增加为了便于考察每个顶点的入度,在顶点表中增加一个入度域,同时设置一个栈来存储所有入度为一个入度域,同时设置一个栈来存储所有入度为 0 0 的的顶点。在进行拓扑排序之前,只要对顶点表扫描一遍顶点。在进行拓扑排序之前,只要对顶点表扫描一遍,将所有入度为,将所有入度为 0 0 的顶点都推入栈中,一旦排序过程的顶点都推入栈中,一旦排序过程中出现新的入度为中出现新的入度为 0 0 的顶点,也同样将其推入栈中。的顶点,也同样将其推入栈中。

v1 0

v2 2 null

v3 1

v4 2

v5 3 null

v6 0

。。。

。。。 。。。

。。。

1 2

34

56

出边表

Page 72: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

11 、扫描顶点表,将入度为、扫描顶点表,将入度为 0 0 的顶点入栈;的顶点入栈;

22 、、 while ( while ( 栈非空 栈非空 ))

{ { 将栈顶顶点将栈顶顶点 vv 弹出并输出之;弹出并输出之;

检查检查 vv 的出边,将每条出边的出边,将每条出边 <v,u><v,u> 终点终点 uu 的入度减的入度减 11,,

若若 uu 的入度变为的入度变为 00 ,则把,则把 uu 推入栈;推入栈;

}}

33 、若输出的顶点数小于、若输出的顶点数小于 nn ,则输出“有回路”;否则拓扑,则输出“有回路”;否则拓扑

排序正常结束。排序正常结束。

拓扑排序算法框架拓扑排序算法框架

Page 73: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

拓扑排序算法int topsort ( Ikgraph *g ){ lkstack s ;pointer p ;int m , i , v ;init_lkstack (& s );for (i =1 ; i<=g->n ; i++)

if ( g->adlist[i].id = = 0 ) push_lkstack (& s , i );m = 0 ;while (! empty_lkstack (& S )){ pop_lkstack (& S ,& v ); printf(“%d ”,v) ; m ++ ; p = g->adlist[v].first ;

while ( p! = NULL ){ g->adlist[p->vertex].id - - ;if ( q->adlist [ p-> vertex ] .id = = 0 ) push_Ikstack (& s , p-> vertex);p = p->next ;}}

if (m<g->n)

{ printf(" 图中有回路,不能拓扑排序!“ ) ;return 0 ;}

else return 1 ;}

Page 74: 基本概念  图的存储结构  图的遍历  生成树  最短路径  拓扑排序

算法分析算法分析

设设 AOVAOV 网有网有 nn 个顶点,个顶点, ee 条边。初始条边。初始建立入度为建立入度为 0 0 的顶点栈,要检查所有顶点的顶点栈,要检查所有顶点一次,执行时间为一次,执行时间为 O(n)O(n) ;排序中,若;排序中,若 AOVAOV网无回路,则每个顶点入、出栈各一次,网无回路,则每个顶点入、出栈各一次,每个边表结点被检查一次,执行时间为每个边表结点被检查一次,执行时间为 O(O(n+e)n+e) ,所以总的时间复杂度为,所以总的时间复杂度为 O(n+e)O(n+e) 。。