第 7 章  图

123
第 7 第 第

description

第 7 章  图. 知 识 点 图的逻辑结构特征及图的基本术语 邻接矩阵和邻接表两种图的存储结构的特点及适用范围 深度优先搜索和广度优先搜索两种遍历算法的特点和执行过程 生成树和最小生成树的概念及构造最小生成树的 prim 和 kruskal 算法 最短路径的含义及求最短路径的算法 拓扑排序的基本思想和步骤 关键路径法及其在管理科学中的作用. 难 点 图的遍历、最小生成树、最短路径、拓朴排序算法的理解 关键路径法求关键活动和关键路径的方法 要 求 熟练掌握以下内容: 图的存储结构 图的遍历算法 了解以下内容: - PowerPoint PPT Presentation

Transcript of 第 7 章  图

第 7 章  图

•知 识 点–图的逻辑结构特征及图的基本术语–邻接矩阵和邻接表两种图的存储结构的特点及适用范围

–深度优先搜索和广度优先搜索两种遍历算法的特点和执行过程

–生成树和最小生成树的概念及构造最小生成树的 prim 和 kruskal 算法

–最短路径的含义及求最短路径的算法–拓扑排序的基本思想和步骤–关键路径法及其在管理科学中的作用

• 难 点– 图的遍历、最小生成树、最短路径、拓朴排序算法的理解

– 关键路径法求关键活动和关键路径的方法• 要 求

熟练掌握以下内容:– 图的存储结构– 图的遍历算法 了解以下内容:– 图的最小生成树和求最小生成树算法的基本思想– 带权有向图的最短路径问题– 利用 AOV 网络的拓朴排序问题– 利用 AOE 网络的关键路径法

7.1 图的定义和术语

7.2 图的存储结构

7.3 图的遍历

7.4 图的连通性问题

7.5 有向无环图及其应用

7.6 最短路径

7.1 图的定义和术语

图是由一个顶点集 V 和一个弧集 V

R 构成的数据结构。 Graph = (V, VR )

其中, VR = {<v,w>| v,w V ∈ 且 P(v,w)}

<v,w> 表示从 v 到 w 的一条弧,并称 v 为弧尾, w 为弧头。 谓词 P(v,w) 定义了弧 <v,w> 的意义或信息。

图的结构定义 :

由于“弧”是有方向的,因此称由顶点集和弧集构成的图为有向图。

E

A

C

B

D

例如 : G1 = (V1, VR1)

其中V1={A, B, C, D, E}

VR1={<A,B>, <A,E>,

<B,C>, <C,D>, <D,B>,

<D,A>, <E,C> }

若 <v, w>VR 必有 <w, v>V

R, 则称 (v,w) 为顶点 v 和顶点 w 之间存在一条边。

B C

A

F E

D

由顶点集和边集构成的图称作无向图。

例如 : G2=(V2,VR2)

V2={A, B, C, D, E, F}

VR2={(A, B), (A, E),

(B, E), (C, D), (D, F),

(B, F), (C, F) }

名词和术语网、子图

完全图、稀疏图、稠密图邻接点、度、入度、出度路径、路径长度、简单路径、简单回路

连通图、连通分量、强连通图、强连通分量生成树、生成森林

A

B E

C F

A

E

A

B

B

C

设图 G=(V,{VR}) 和图 G=(V,{VR}),

且 VV, VRVR,

则称 G 为 G 的子图。

15 9

7 2111

32

弧或边带权的图分别称作有向网或无向网。

假设图中有 n 个顶点, e 条边,则

含有 e=n(n-1)/2 条边的无向图称作完全图 ;

含有 e=n(n-1) 条弧的有向图称作 有向完全图 ;

若边或弧的个数 e<nlogn ,则称作稀疏图,否则称作稠密图。

假若顶点 v 和顶点 w 之间存在一条边,则称顶点 v 和 w 互为邻接点,

例如 :

TD(B) = 3

TD(A) = 2

边 (v,w) 和顶点 v 和 w 相关联。 和顶点 v 关联的边的数目定义为 v的度。

A

C

D

F E

B右侧图中

顶点的出度 : 以顶点 v 为弧尾的弧的数目 ;

A

B E

C F

对有向图来说,

顶点的入度 : 以顶点 v为弧头的弧的数目。顶点的度 (TD)=出度 (OD)+ 入度 (ID)

例如 :

ID(B) = 2

OD(B) = 1

TD(B) = 3

由于弧有方向性,则有入度和出度之分

设图 G=(V,{VR}) 中的一个顶点序列{ u=vi,0,vi,1, …, vi,m=w} 中, (vi,j-1,vi,j)VR 1≤j≤m,

则称从顶点 u 到顶点 w 之间存在一条路径。路径上边的数目称作路径长度。

A

B E

C F

如 : 从 A到 F长度为 3 的路径 {A,B,C,F}

简单路径 : 指序列中顶点不重复出现的路径。

简单回路 : 指序列中第一个顶点和最后一个顶点相同的路径。

若图 G中任意两个顶点之间都有路径相通,则称此图为连通图 ;

若无向图为非连通图,则图中各个极大连通子图称作此图的连通分量。

B

A

C

D

F EB

A

C

D

F E

若任意两个顶点之间都存在一条有向路径,则称此有向图为强连通图。

A

B E

C F

A

B E

C F

对有向图,

否则,其极大强连通子图称作它的强连通分量。

假设一个连通图有 n 个顶点和 e 条边,其中 n-1 条边和 n 个顶点构成一个极小连通子图,称该极小连通子图为此连通图的生成树。

对非连通图,则称由各个连通分量的生成树的集合为此非连通图的生成森林。

B

A

C

D

F E

7.2 图的存储结构

一、图的数组 ( 邻接矩阵 ) 存储表示

二、图的邻接表存储表示

三、有向图的十字链表存储表示

四、无向图的邻接多重表存储表示

B

A

C

D

F E

一、图的数组 (邻接矩阵 )存储表示

•邻接矩阵是表示顶点之间相邻关系的矩阵。所谓两顶点的相邻关系即它们之间有边相连。

•邻接矩阵是一个( n×n)阶方阵, n为图的顶点数,它的每一行分别对应图的各个顶点,它的每一列也分别对应图的各个顶点。我们规定矩阵的元素为:

Aij={0 (i,j)VR

1 (i,j)VR

一、图的数组 (邻接矩阵 )存储表示

B

A

C

D

F E

定义 : 矩阵的元素为

0 1 0 0 1 01 0 0 0 1 00 0 0 1 0 10 0 1 0 0 11 1 0 0 0 00 1 1 1 0 0

A B C D E F

ABCDEF

1

有向图的邻接矩阵为非对称矩阵

A

B E

C F

0 1 0 1 00 0 1 0 00 0 0 0 10 0 1 0 01 1 0 0 0

#define INFINITY INT_MAX // 最大值∞#define MAX_CERTEX_NUM 20

// 最大顶点数typedef enum{DG,DN,UDG,UDN} GraphKind;

//{ 有向图 , 有向网 , 无向图 , 无向网 }

图的邻接矩阵存储表示

typedef struct ArcCell { // 弧的定义 VRType adj; // VRType 是顶点关系类型。 // 对无权图,用 1 或 0 表示相邻否; // 对带权图,则为权值类型。 InfoType *info; // 该弧相关信息的指针 } ArcCell, AdjMatrix[MAX_VERTEX_NU

M]

[MAX_VERTEX_NUM];

typedef struct { // 图的定义 VertexType // 顶点信息 vexs[MAX_VERTEX_NUM];

AdjMatrix arcs; // 弧的信息 int vexnum, arcnum; // 顶点数,弧数 GraphKind kind; // 图的种类标志 } MGraph;

1. 存储空间• 无向图的邻接矩阵是对称的,如果 A[i ,j]=1 ,必有 A[j , i]=1 。这说明,只输入和存储其上三角阵元素即可得到整个邻接矩阵。其存储空间仅需 n(n-1)/2

• 一般有向图的邻接矩阵是不对称的, A[i ,j] 不一定等于 A[j , i] 。其存储空间需 n2

• 对于稀疏图而言,不适于用邻接矩阵来存储,因为这样会造成存储空间的浪费。

邻接矩阵表示法的特点

2.便于运算•便于判断判断图中任意两顶点是否有边 (或弧 )相连 .即 A[i, j]的值是 0 还是 1

•便于求得各顶点的度无向图 :其邻接矩阵第 i行 1的个数的和就是图中第 i个顶点的度 .即

有向图 :第 i行 1的个数的和是第 i个顶点的出度

第 i列 1的个数的和是第 i个顶点的入度

邻接矩阵表示法的特点

3.便于实现图的一些基本操作 如: FirstAdjVertex(G, v):• 首先,由 LocateVertex(G, v)找到v在图中的位置,即 v在一维数组 vexs 中的序号I。

• 二维数组arcs 中第 i行上第一个 adj域非零的分量所在的列号 j ,便是v的第一个邻接点在图G中的位置。

• 取出一维数组vexs[j] 中的数据信息,即与顶点v邻接的第一个邻接点的信息。

邻接矩阵表示法的特点

基本思想•邻接表是图的一种链接存储结构。•对于图中存在的边信息则存储起来,而对于不相邻接的顶点则不保留信息。在邻接表中,对图中的每个顶点建立一个带头结点的单链表,如第 i个单链表中的结点则表示依附于顶点 vi 的边(若是有向图,则表示以 vi 为弧尾的弧)。每个单链表的头结点又构成一个表头结点表。

二、图的邻接表存储表示

一个 n个顶点的图的邻接表表示需要有两部分构成:①表头结点表由所有表头结点以顺序结构(向量)的形式存储,以便可以随机访问任一顶点的单链表 ②表示图中顶点间邻接关系的 n个单链表(即边表或弧表 )

结构组成

typedef struct ArcNode {

int adjvex; // 该弧所指向的顶点的位置 struct ArcNode *nextarc;

// 指向下一条弧的指针 InfoType *info; // 该弧相关信息的指针} ArcNode;

adjvex nextarc info弧结点的结构弧结点的结构

typedef struct VNode {

VertexType data; // 顶点信息 ArcNode *firstarc;

// 指向第一条依附该顶点的弧 } VNode, AdjList[MAX_VERTEX_NUM];

data firstarc表头结点的结构表头结点的结构

typedef struct {

AdjList vertices;

int vexnum, arcnum;

int kind; // 图的种类标志 } ALGraph;

图的结构定义 ( 邻接表 )

D

B

A

C

F E A 1 4

B 0 4 5

C 3 5

D 2 5

E 0 1

F 1 2 3

0 1 2 3 4 5

示例

有向图的邻接表

1 4

2

3

0 1

2

0 1 2 3 4

A

B

C

F

E

A

B E

C F

可见,在有向图的邻接表中不易找到指向该顶点的弧

•对于有 n个顶点, e条边的无向图而言,若采取邻接表作为存储结构,则需要 n个表头结点和 2e个表结点。很显然在边很稀疏(即 e 远小于 n(n-1)/2 时)的情况下,用邻接表存储所需的空间要比邻接矩阵所需的 n(n-1)/2要节省得多。 •在有向图邻接表中,一条弧对应一个表结点,表结点的数目和弧的数目相同。

存储空间

•在无向图的邻接表中,各顶点对应的单链表的结点数(不算表头结点)就等于该顶点的度数。

无向图的度

有向图的度

•在有向图邻接表中,单链表的结点数就等于相应顶点的出度数。

•要求有向图中某顶点的入度数,需扫视邻接表的所有单链表,统计与顶点标号相应的结点个数。

解决方法 :再建立一个逆邻接表

A

B E

C D

有向图的逆邻接表

A

B

C

D

E

3 0

3

4

2

0

0

1

2

3

4

在有向图的邻接表中,对每个顶点,链接的是指向该顶点的弧

1 ^

三、有向图的十字链表存储表示

十字链表 (Orthogonal List) 是有向图的另一种链式存储结构。我们也可以把它看成是将有向图的邻接表和逆邻接表结合起来形成的一种链表。有向图中的每一条弧对应十字链表中的一个弧结点,同时有向图中的每个顶点在十字链表中对应有一个结点,叫做顶点结点。

弧的结点结构弧的结点结构

弧尾顶点位置 弧头顶点位置 弧的相关信息

指向下一个有相同弧尾的结点

指向下一个有相同弧尾的结点

指向下一个有相同弧头的结点

指向下一个有相同弧头的结点

typedef struct ArcBox { // 弧的结构表示 int tailvex, headvex; InfoType *info;

struct ArcBox *hlink, *tlink;

} VexNode;

tailvex headvex hlink tlink info

顶点的结点结构顶点的结点结构

顶点信息数据

指向该顶点的第一条入弧指向该顶点的第一条入弧

指向该顶点的第一条出弧指向该顶点的第一条出弧

typedef struct VexNode { // 顶点的结构表示 VertexType data;

ArcBox *firstin, *firstout;

} VexNode;

data firstin firstout

typedef struct {

VexNode xlist[MAX_VERTEX_NUM];

// 顶点结点 ( 表头向量 )

int vexnum, arcnum;

// 有向图的当前顶点数和弧数} OLGraph;

有向图的结构表示 ( 十字链表 )

示例 A

B

C

A

B

C

0 1 2 ∧

0 2 ∧ ∧

0 1

2 1 ∧

2 0 ∧ ∧

7.3 图的遍历

遍历的方法

深度优先搜索

广度优先搜索

从图中某个顶点 V0 出发,访问

此顶点,然后依次从 V0 的各个未被访问

的邻接点出发深度优先搜索遍历图,直至图中所有和 V0 有路径相通的顶点都被访

问到。

一、深度优先搜索遍历图

连通图的深度优先搜索遍历

V1

V2 V3

V4

V8

V5 V7V6

v2v1 v8 v5v4 v3 v6 v7

深度优先遍历序列 :

从上页的图解可见 :

1. 从深度优先搜索遍历连通图的过程类似于树的先根遍历 ;

解决的办法是 : 为每个顶点设立一个 “访问标志 visited[w]”;

2. 如何判别 V 的邻接点是否被访问?

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);

// 对 v 的尚未访问的邻接顶点 w

// 递归调用 DFS

} // DFS

首先将图中每个顶点的访问标志设为 FALSE, 之后搜索图中每个顶点,如果未被访问,则以该顶点为起始点,进行深度优先搜索遍历,否则继续检查下一顶点。

非连通图的深度优先搜索遍历

a b

c

h

d e

k

f

g

8

1

2 3 4 5

6

7

0

F F F F F F F F F0 1 2 3 4 5 6 7 8

T T T T T T T T T

a c h d k f e b g

a

c

h k

fed

bg

访问标志 :

访问次序 :

例如 : a

c

h

d k

f e

二、广度优先搜索遍历图

V

w1

w8

w3

w7

w6

w2

w5

w4

对连通图,从起始点 V 到其余各顶点必定存在路径。 其中, V->w1, V->w2, V->w8

的路径长度为 1;V->w7, V->w3, V->w5

的路径长度为 2;V->w6, V->w4 的路径长度为 3 。

w1

V

w2

w7

w6

w3

w8

w5

w4

从图中的某个顶点 V0 出发,并在访问此顶点之后依次访问 V0 的所有未被访问过的邻接点,之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和 V0 有路径相通的顶点都被访问到。 若此时图中尚有顶点未被访问 , 则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。

V1

V2 V3

V4

V8

V5 V7V6

v2v1 v4 v5v3 v6 v7 v8

广度优先遍历序列 :

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

… …

visited[v] = TRUE; Visit(v); // 访问 uEnQueue(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

三、遍历应用举例

1. 求一条从顶点 i 到顶点 s

的 简单路径2. 求两个顶点之间的一条路径 长度最短的路径

1. 求一条从顶点 i 到顶点 s 的简单路径

a

b

c

h

d e

k

f

g 求从顶点 b 到顶点 k 的一条简单路径。 从顶点 b 出发进行深度优先搜索遍历

例如 :

假设找到的第一个邻接点是 a, 且得到的结点访问序列为 : b a d h c e k f g

假设找到的第一个邻接点是 c,则得到的结点访问序列为 : b c h d a e k f g

1. 从顶点 i 到顶点 s , 若存在路径,则从顶点 i 出发进行深度优先搜索,必能搜索到顶点 s 。

2. 遍历过程中搜索到的顶点不一定是路径上的顶点。

结论 :结论 :

3. 由它出发进行的深度优先遍历已经完成的顶点不是路径上的顶点。

2. 求两个顶点之间的一条路径 长度最短的路径

若两个顶点之间存在多条路径,则其中必有一条路径长度最短的路径。如何求得这条路径 ?

因此,求路径长度最短的路径可以基于广度优先搜索遍历进行,但需要修改链队列的结点结构及其入队列和出队列的算法。

ab

c

hd

ek

fg 深度优先搜索访问顶

点的次序取决于图的存储结构,而广度优先搜索访问顶点的次序是按“路径长度”渐增的次序。

例如 :

例如 : 求右图中顶点 3

至顶点 5 的一条最短路径。链队列的状态如下所示 :

Q.front

3 1 2 4 7 5

3 2

1

4 7

5 6 8 9

Q.rear

1) 将链队列的结点改为“双链”结点。即结点中包含 next 和 priou 两个指针 ;

2) 修改入队列的操作。插入新的队尾结点时,令其 priou 域的指针指向刚刚出队列的结点,即当前的队头指针所指结点 ;

3) 修改出队列的操作。出队列时,仅移 动队头指针,而不将队头结点从链表中删除。

7.4 图的连通性问题无向图的连通分量

图 例

即 ,

深度优先生成树 :由深度优先搜索得到的

V1

V2

V4

V8

V5

V3

V7

V6

V1

V2 V3

V4 V5

V8

V6 V7

广度优先生成树 : 由广度优先搜索得到的非连通图的生成森林

生成树

最小生成树

假设要在 n 个城市之间建立通讯联络网,则连通 n 个城市只需要修建 n-1 条线路,如何在最节省经费的前提下建立这个通讯网?

问题:

构造网的一棵最小生成树,即: 在 e 条带权的边中选取 n-1 条边(不构成回路),使“权值之和”为最小。

算法二:克鲁斯卡尔 (Kruskal) 算法

该问题等价于:

算法一:普里姆( Prim )算法

取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点 w 和已经在生成树上的顶点 v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n 个顶点为止。

普里姆算法的基本思想 :

a b

c

d

e

g

f

195

1418

27

16 8

21

3

127

例如 :

a

e

d

c

b

g

f

14

8

5

316

21

所得生成树权值和 = 14+8+3+5+16+21 = 67

在生成树的构造过程中,图中 n 个顶点分属两个集合:已落在生成树上的顶点集 U 和尚未落在生成树上的顶点集 V-U ,则应在所有连通 U 中顶点和 V-U 中顶点的边中选取权值最小的边。

一般情况下所添加的顶点应满足下列条件 :

U

V-U

设置一个辅助数组,对当前 V

- U 集中的每个顶点,记录和顶点集U 中顶点相连接的代价最小的边:

struct {

VertexType adjvex; // U 集中的顶点 VRType lowcost; // 边的权值 } closedge[MAX_VERTEX_NUM];

a bc

de

gf

19 514

18

27

16 8

21

3

127

closedge0 1 2 3 4 5 6

Adjvex

Lowcost

a

ed

cb

a a a

19 14 1814

例如 :

e

12

e e

8 168

d

3

d d

7 213

c

55

∞ 19 ∞ ∞ 14 ∞ 1819 ∞ 5 7 12 ∞ ∞ ∞ 5 ∞ 3 ∞ ∞ ∞ ∞ 7 3 ∞ 8 21 ∞14 12 ∞ 8 ∞ ∞ 16 ∞ ∞ ∞ 21 ∞ ∞ 2718 ∞ ∞ ∞ 16 27 ∞

0 12

3

4

56

16

gf

21

具体做法 : 先构造一个只含 n 个顶点的子图 SG ,然后从权值最小的边开始,若它的添加不使 SG 中产生回路,则在 SG 上加上这条边,如此重复,直至加上 n-1 条边为止。

考虑问题的出发点 : 为使生成树上边的权值之和达到最小,则应使生成树中每一条边的权值尽可能地小。

克鲁斯卡尔算法的基本思想:

a b

c

d

e

g

f

195

1418

27

16 8

21

3

127

a

e

d

c

b

g

f

14

8

5

316

21

例如 :

712

18

19

算法描述 :构造非连通图 ST=( V,{ } );

k = i = 0; // k 计选中的边数 while (k<n-1) {

++i;

检查边集 E 中第 i 条权值最小的边 (u,v);

若 (u,v) 加入 ST 后不使 ST 中产生回路, 则 输出边 (u,v); 且 k++;

}

普里姆算法 克鲁斯卡尔算法

稠密图 稀疏图

算法名

适应范围

比较两种算法

7.5 有向无环图及其应用

一个无环的有向图 , 称为有向无环图 (DAG 图 )

如 :

有向树 DAG 图 有向图

有向无环图的应用有向无环图是描述一项工程或系统的进行过程的有效工具 .除最简单的情况之外 ,几乎所有的工程都可分为若干个称做活动 (activity) 的子工程 ,而这些子工程之间 ,通常受着一定条件的约束 , 如其中某些子工程的开始必须在另一些子工程完成之后 .对整个工程和系统 ,人们关心的是 :工程能否顺利进行

估算整个工程完成所必须的最短时间

拓朴排序关键路径

拓扑排序

AOV 网

AOV 网图例

AOV网图例 :某专业课程设置

C2

C3

C4

C5

C6

C7

C8

C1

课程代号

普通物理

计算机原理

程序设计

离散数学

数据结构

编译技术

操作系统

高等数学

课程代号

C1

C2

C1, C4

C4, C5

C4, C6

C3, C6

先行课程

AOV网图例 :某专业课程设置

C1

C2 C3

C4

C5 C6

C7 C8

( 2) AOV网络中一定不能有有向环路。3

拓扑排序

•所谓“拓扑排序”就是将 AOV 网络中的各个顶点 (各个活动 )排列成一个线性有序序列,使得所有要求的前趋、后继关系都能得到满足。

•通过拓扑排序还可以判断出此 AOV 网络是否包含有有向环路,若有向图 G所有顶点都在拓扑排序序列中,则 AOV 网络必定不包含有有向环路。

拓扑排序方法(1) 在网络中选择一个没有前趋的顶点,并把它输出;(2) 从网络中删去该顶点和从该顶点发出的所有有向弧;(3) 重复执行上述两步,直到网中所有的顶点都被输出 (此时,原 AOV 网络中的所有顶点和边就都被删除掉了 )。如果进行到某一步,无法找到无前趋的顶点,则说明此 AOV 网络中存在有向环路,遇到这种情况,拓扑排序就无法进行了。

a

b

c

g

h

d

f

e

a b h c d g f e

在算法中需要用定量的描述替代定性的概念 没有前驱的顶点 入度为零的顶点删除顶点及以它为尾的弧 弧头顶点的入度减 1

拓扑排序过程

拓扑排序算法实现

• 邻接矩阵存储结构

• 邻接表存储结构

邻接矩阵存储结构

v1 v2

v3v4

v6 v5

000000

0 0 0 0 0

1

00000

2

00 0 0 0

0

000

3

0 0 0

000

4

0 0

00

5

0

6

邻接表存储结构

邻接表存储结构

邻接表存储结构v1 v2

v3v4

v6 v5

∧ ∧

0

2

2

1

3 ∧

0 V1

1 V2

2 V3

3 V4

4 V5

5 V6 0

2 31

1 4

4 ∧

3 4 ∧v1v6

1

2

1

0

v3

0

v41

0

v2

0v5

关键路径

AOE网可用来估算工程的完成时间

a

b

c

d

e

f

g

h

k

6

4

5

2

1

1

8

7

2

4

4

AOE网有待研究的问题

( 1)完成整项工程至少需要多长时间

( 2)哪些活动是影响工程进度的关键

整个工程完成的时间为:从有向图的源点到汇点的最长路径。

a

b

c

d

e

f

g

h

k

6

4

5

2

1

1

8

7

2

4

4

例如 :

“关键活动”指的是:该弧上的权值增加 将使有向图上的最长路径的长度增加。

源点

汇点6 1

7 4

如何求关键活动?

什么是“关键活动” ?

该活动的最早开始时间

= 该活动的最迟开始时间

i jdut(<i,j>)

“事件 ( 顶点 )” 的 最早发生时间 ve(j)

ve(j) = 从源点到顶点 j 的最长路径长度 ;

“事件 ( 顶点 )” 的 最迟发生时间 vl(k)

vl(k) = 从顶点 k 到汇点的最短路径长度 ;

假设第 i 条弧为 <j, k>

则 对第 i 项活动言 “活动 ( 弧 )” 的 最早开始时间 ee(i)

ee(i) = ve(j);

“活动 ( 弧 )” 的 最迟开始时间 el(i)

el(i) = vl(k) – dut(<j,k>);

事件发生时间的计算公式 :

ve( 源点 ) = 0;

ve(k) = Max{ve(j) + dut(<j, k>)}

vl( 汇点 ) = ve( 汇点 );

vl(j) = Min{vl(k) – dut(<j, k>)}

a

b

c

d

e

f

g

h

k

6

4

5

2

1

1

8

7

2

4

4

a b c d e f g h kvevl

0 0 0 0 0 0 0 0 06 4 5 7 1157 15 14 18

181818181818181818 16 1486 6 1080 7

拓扑有序序列 : a - d - f - c - b - e - h - g - k

a b c d e f g h kvevl

0 6 4 5 7 7 15 14 18

1814161078660

ab ac ad be ce df eg eh fh gk hk权 6 4 5 1 1 2 8 7 4 2 4e

l0 0 0 6 4 5 7 7 7 15 14

14160 2 3 6 6 8 8 7 10

算法的实现要点 :

显然,求 ve 的顺序应该是按拓扑有序的次序 ; 而 求 vl 的顺序应该是按拓扑逆序的次序 ;

因为 拓扑逆序序列即为拓扑有序序列的 逆序列,

因此 应该在拓扑排序的过程中, 另设一个“栈”记下拓扑有序序列。

7.6 最短路径

• 求从某个源点到其余各点的最短路径

• 每一对顶点之间的最短路径

从源点到其余各点的最短路径

45

50 10

20 3530

315

151020

求从源点到其余各点的最短路径的算法的基本思想 :

依最短路径的长度递增的次序求得各条路径

源点

v1

…其中,从源点到顶点 v1 的最短路径是所有最短路径中长度最短者。

v2

在这条路径上,必定只含一条弧,并且这条弧的权值最小。

下一条路径长度次短的最短路径的特点 :

路径长度最短的最短路径的特点 :

它只可能有两种情况 : 或者是直接从源点到该点 ( 只含一条弧 ) ; 或者是,从源点经过顶点 v1 ,再到达该顶点 ( 由两条弧组成 ) 。

其余最短路径的特点:

再下一条路径长度次短的最短路径的特点 :

它可能有三种情况:或者是,直接从源点到该点 ( 只含一条弧 ) ; 或者是,从源点经过顶点 v1 ,再到达该顶点 ( 由两条弧组成 ) ;或者是,从源点经过顶点 v2 ,再到达该顶点。 它或者是直接从源点到该点 ( 只含一条弧 ) ; 或者是,从源点经过已求得最短路径的顶点,再到达该顶点。

求最短路径的迪杰斯特拉算法:

一般情况下,Dist[k] = < 源点到顶点 k 的弧上的权值 >

或者 = < 源点到其它顶点的路径长度 >

+ < 其它顶点到顶点 k 的弧上的权值 >

设置辅助数组 Dist ,其中每个分量 Dist

[k] 表示 当前所求得的从源点到其余各顶点 k 的最短路径。

1 )在所有从源点出发的弧中选取一条权值最小的弧,即为第一条最短路径。

2 )修改其它各顶点的 Dist[k] 值。假设求得最短路径的顶点为 u ,若 Dist[u]+G.arcs[u][k]<Dist[k]

则将 Dist[k] 改为 Dist[u]+G.arcs[u][k]

INFINITY

kvarcsGkDist

]][0[.][

V0 和 k 之间存在弧V0 和 k 之间不存在弧

其中的最小值即为最短路径的长度。

•狄克斯特拉算法需要一个顶点集合,初始时集合内只有一个源点 V0 ,以后陆续将已求得最短路径的顶点加入到集合中,到全部顶点都进入集合了,过程就结束了。

•集合可用一维数组来表示,设此数组为 S,凡在集合 S以外的顶点,其相应的数组元素 S[i] 为 0,否则为 1。

•还需要另一个一维数组 dist,每个顶点对应数组的一个单元,记录从源点到其他各顶点当前的最短路径长度,其初值为 dist[i]=cost[V0][i] , i=2…n 。数组 dist中的数据随着算法的逐步进行要不断地修改。

•定义了 S集合和 dist数组并对其初始化后,狄克斯特拉算法在进行中,都是从 S之外的顶点集合中选出一个顶点 w,使 dist[w] 的值最小。于是从源点到达 w只通过 S中的顶点,把 w加入集合 S中,并调整 dist中记录的从源点到集合中每个顶点 v的距离:从原来的 dist[v] 和 dist[w]+cost[w][v] 中,选择较小的值作为新的 dist[v] 。

•重复上述过程,直到 S中包含 V中其余各顶点的最短路径。

18

④ ①

② ③

5 10

24

25

8

6 9

7 4

12 15

•以所示的图为例来说明当指定以 V6为源点V0后,用狄克斯特拉算法求最短路径的动态执行情况,其表示集合的数组 S和表示距离的数组 dist元素值的变化如图所示。

dist 5 ∞ 25 0 25 ∞

0 0 0 0 1 0

1 2 3 4 5 6

S

算法动态执行情况

dist 5 12 25 0 23 ∞

1 0 0 0 1 0

1 2 3 4 5 6

S

dist 5 12 25 0 21 ∞

1 1 0 0 1 0

1 2 3 4 5 6

S

算法动态执行情况

dist 5 12 25 0 21 ∞

1 1 0 0 1 1

1 2 3 4 5 6 S

dist 5 12 25 0 21 ∞

1 1 1 0 1 1

1 2 3 4 5 6 S

求每一对顶点之间的最短路径

弗洛伊德算法的基本思想是:

从 vi 到 vj 的所有可能存在的路径中,选出一条长度最短的路径

若 <vi,vj> 存在,则存在路径 {vi,vj}

// 路径中不含其它顶点若 <vi,v1>,<v1,vj> 存在,则存在路径 {vi,v1,v

j}

// 路径中所含顶点序号不大于 1

若 {vi,…,v2}, {v2,…,vj} 存在, 则存在一条路径 {vi, …, v2, …vj}

// 路径中所含顶点序号不大于 2 … 依次类推,则 vi 至 vj 的最短路径应是上述这些路径中,路径长度最小者。

小 结• 图• 图的存储结构

– 邻接矩阵– 邻接表

• 图的遍历– 深度优先搜索– 广度优先搜索

• 图的应用– 最小生成树– 最短路径问题– 利用 AOV 网络研究拓扑排序问题– 利用 AOE 网络研究关键路径的方法