slide1
Download
Skip this Video
Download Presentation
第七章 图

Loading in 2 Seconds...

play fullscreen
1 / 109

第七章 图 - PowerPoint PPT Presentation


  • 112 Views
  • Uploaded on

理解图的基本概念及有关术语,掌握图的四种存储结构的表示方法; 熟练掌握图的两种遍历(深度优先搜索和广度优先搜索),能列出按上述两种遍历算法得到的序列; 理解最小生成树的概念,能按 Prim 算法构造最小生成树; 掌握求最短路径、拓扑排序以及关键路径的算法思想。. 第七章 图. 学习要点. 7.1 图的的定义和术语. 图的定义. 图是由 一个 顶点集 V 和 一个描述顶点之间关系(边或者弧)的集合 R 构成的数据结构。 Graph = ( V , VR ) 其中, VR = {<v,w>| v,w∈V 且 P(v,w)}

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' 第七章 图' - tex


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1
理解图的基本概念及有关术语,掌握图的四种存储结构的表示方法;理解图的基本概念及有关术语,掌握图的四种存储结构的表示方法;

熟练掌握图的两种遍历(深度优先搜索和广度优先搜索),能列出按上述两种遍历算法得到的序列;

理解最小生成树的概念,能按Prim算法构造最小生成树;

掌握求最短路径、拓扑排序以及关键路径的算法思想。

第七章 图

学习要点

slide2
7.1 图的的定义和术语
  • 图的定义

图是由一个顶点集 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>的意义或信息。

slide3

V0

V1

V2

V3

V1

V0

V2

V4

V3

  • 图的应用举例

例1 交通图(公路、铁路)

顶点:地点

边:连接地点的公路

例2 电路图

顶点:元件

边:连接元件之间的线路

例3 各种流程图

如产品的生产流程图

顶点:工序

边:各道工序之间的顺序关系

slide4

V1

V0

V2

V4

V3

  • 有向图和无向图

在图中,若用箭头标明了边是有方向性的,则称这样的图为有向图,否则称为无向图。

 在无向图中,一条边(x,y)与(y,x)表示的结果相同,用圆括号表示。例如:

G1=<V1, E1>

V1={v0, v1, v2, v3, v4 }

E1={(v0,v1),(v0,v3),(v1,v2),(v1,v4),(v2,v3),(v2,v4)}

slide5

V0

V1

V2

V3

  • 有向图和无向图

 在有向图中,一条边<x,y>与<y,x>表示的结果不相同,用尖括号表示。<x,y>表示从顶点x出发向顶点y的边,x为始点,y为终点。例如:

G2=<V2, A2>

V2={v0, v1, v2, v3}

A2={<v0,v1 >, <v0,v2 >, <v2,v3 >, <v3,v0 >}

slide6

名词和术语

顶点、边、弧、弧头、弧尾

完全图、稠密图、稀疏图

度、入度、出度

边的权、网图

路径、路径长度

回路、简单路径、简单回路

子图

连通图、连通分量

强连通图、强连通分量

生成树、生成森林

slide7

W

V

  • 顶点、边、弧、弧头、弧尾

 图中的数据元素通常称为顶点;

若<v,w>∈VR,则<v,w>表示从v到w的一条弧,且称v为弧尾或初始点,称w为弧头或终端点。

若<v,w>∈VR,必有则<w,v>∈VR,即VR是对称的,则以无序对(v,w)代替这两个有序对,表示从v和w的一条边。

W

V

slide8

2

2

1

3

1

3

  • 完全图、稠密图、稀疏图

在一个无向图中,如果任意两顶点都有一条直接边相连接,则称该图为无向完全图。

在一个有向图中,如果任意两顶点之间都有方向互为相反的两条弧相连接,则称为有向完全图。

若一个图接近完全图,称为稠密图;称边数很少的图为稀疏图。   

有向完全图

无向完全图

slide9

1

5

7

2

4

5

3

2

4

6

1

3

6

  • 度、入度、出度

 在图中,一个顶点依附的边或弧的数目,称为该顶点的度。在有向图中,以顶点V为起点的有向边数称为顶点V的出度,以顶点V为终点的有向边数称为顶点V的入度,入度和出度之和称为该顶点的度。

slide10

2

B

A

1

4

1

3

3

4

1

5

5

5

6

3

2

8

C

4

2

7

  • 边的权、网

与边有关的数据信息称为权。

边上带权的图称为网。

(b)有向网

(a) 无向网

slide11

V1

V1

V0

V2

V0

V4

V3

  • 路径、路径长度

无向图中从顶点v到v`的路径是一个顶点序列(v=vi,0,vi,1,…,vi,m=v`),其中(vi,j-1,vi,j)∈E(G) 。在有向图中路径也是有向的,它是由若干条弧组成。路径上边或弧的数目称为路径长度。

slide12

V0

V1

V2

V3

V1

V0

V2

V4

V3

  • 回路、简单路径、简单回路

 起点和终点相同的路径称为回路或者环。序列中顶点不重复出现的路径称为简单路径。除第一个顶点与最后一个顶点之外,其他顶点不重复出现的回路称为简单回路。

无向图G1

有向图G2

slide13

V1

V1

V0

V0

V2

V4

V3

V3

V1

V0

V2

V4

V3

  • 子图

 若有两个图G和G`,G=(V,E),G`=(V`,E` ), 满足如下条件:V`V,E`E,即V`为V的子集, E`为E的子集,称图G`为图G的子图。

例 (b)、(c) 是 (a) 的子图

(a)

(b)

(c)

slide14

V0

V4

V3

V0

V2

V3

V5

V4

G2的两个连通分量

V2

V1

V1

  • 连通图、连通分量

 在无向图G中,如果从顶点v到顶点v`有路径,则称v和v`是连通的。若图中任意两个顶点都是连通的,则称G为连通图。

 无向图中的极大连通子图称该图的连通分量。

连通图G1

非连通图G2

slide15

V3

V1

V0

V2

V2

V1

V3

V3

V2

V1

V0

V0

  • 强连通图、强连通分量

 在有向图G中,如果对于任意一对顶点vj和vi 均有从一个顶点vj到另一个顶点vi有路径,也有从vi到 vj的路径,则称该有向图是强连通图。

有向图的极大强连通子图称为强连通分量。

强连通图G1

非强连通图G2

G2的两个强连通分量

slide16

V0

V4

V3

V0

V4

V3

V0

V4

V3

V2

V2

V2

V1

V1

V1

  • 生成树、生成森林

 连通图G的生成树,是G的包含其全部n 个顶点的一个极小连通子图。它必定包含且仅包含G的n-1条边。

 极小连通子图意思是:该子图是G 的连通子图,在该子图中删除任何一条边,子图不再连通。

 非连通图的生成树则组成一个生成森林。若图中有n个顶点,m个连通分量,则生成森林中有n-m条边。

连通图 G1

G1的生成树

slide17

2

4

5

1

3

6

1

5

7

2

4

6

3

  • 例:

路径:1,2,3,5,6,3

路径长度:5

简单路径:1,2,3,5

回路:1,2,3,5,6,3,1

简单回路:3,5,6,3

G1

路径:1,2,5,7,6,5,2,3

路径长度:7

简单路径:1,2,5,7,6

回路:1,2,5,7,6,5,2,1

简单回路:1,2,3,1

G2

slide18
7.2 图的存储表示

 图是一种结构复杂的数据结构,表现在不仅各个顶点的度可以千差万别,而且顶点之间的逻辑关系也错综复杂。从图的定义可知,一个图的信息包括两部分,即图中顶点的信息以及描述顶点之间的关系(边或者弧)的信息。因此无论采用什么方法建立图的存储结构,都要完整、准确地反映这两方面的信息。

 图通常有邻接矩阵、邻接表、邻接多重表和十字链表等表示方法。  

slide19

1 若(vi,vj)或<vi,vj>是E(G)中的边

0 若(vi,vj)或<vi,vj>不是E(G)中的边

A[i][j]=

7.2.1 邻接矩阵

定义:在邻接矩阵表示中,除了用一维数组存放顶点本身信息外,还用一个矩阵表示各个顶点之间的邻接关系。这个矩阵称为邻接矩阵。

 假设图G=(V, E)有n个确定的顶点,即V={v0,v1,…, vn-1} ,则表示G中各顶点相邻关系为一个n×n的矩阵,矩阵的元素为 :

slide20

V0

V1

V2

V3

V1

V0

V2

V3

V4

7.2.1 邻接矩阵

A=

B=

slide21

5

V3

V0

7

6

8

V4

3

V1

V2

9

4

7.2.1 邻接矩阵

若G是带权图,则邻接矩阵定义为:

wij若(vi,vj)或<vi,vj>是E(G)中的边

∞ 若(vi,vj)或<vi,vj>不是E(G)中的边

其中 wij表示边上的权值;

∞表示大于所有边上权值的数。

A[i][j]=

C=

slide22

7.2.1 邻接矩阵

从无向图的邻接矩阵可以得出如下结论:

  • 矩阵是对称的;
  • 第i行或第i 列1的个数为顶点i 的度;
  • 矩阵中1的个数的一半为图中边的数目;
  • 很容易判断顶点i和顶点j之间是否有边相连(看矩阵中i行j列值是否为1)。
slide23

7.2.1 邻接矩阵

从有向图的邻接矩阵可以得出如下结论:

  • 矩阵不一定是对称的;
  • 第i 行中1的个数为顶点i 的出度;
  • 第i列中1的个数为顶点 i的入度;
  • 矩阵中1的个数为图中弧的数目;
  • 很容易判断顶点i和顶点j是否有弧相连。
slide24

7.2.1 邻接矩阵

图的邻接矩阵数据类型描述:

在用邻接矩阵存储图时,除了用一个二维数组存储用于表示顶点间相邻关系的邻接矩阵外,还需用一个一维数组来存储顶点信息,另外还有图的顶点数和边数。故可将其形式描述如下:

简化后的邻接矩阵的存储表示:

#define INFINITY 1000

#define MAX_VERTEX_NUM 20

slide25

7.2.1 邻接矩阵

typedef char VertexType; // 顶点的定义

typedef int VRType; // 弧的定义

typedef struct {

VertexType vexs[MAX_VERTEX_NUM];

// 顶点信息

VRType arcs [MAX_VERTEX_NUM] [MAX_VERTEX_NUM];

// 边的信息

int vexnum, arcnum; // 顶点数,弧数

} MGraph;

slide26

7.2.2 邻接表

 邻接表是图的一种顺序存储与链式存储结合的存储方法。邻接表表示法类似于树的孩子链表表示法。

就是对于图G中的每个顶点vi,将所有邻接于vi的顶点链成一个单链表,这个单链表就称为顶点vi的邻接表,再将所有的邻接表表头放到数组中,就构成了图的邻接表。其中,单链表中的结点称为表结点,每个单链表设的一个头结点称为顶点结点。

slide27

一、无向图的邻接表

 对图中每个顶点Vi建立一个单链表,链表中的结点表示依附于顶点Vi的边,每个链表结点为两个域:

 每个链表附设一个头结点,头结点结构为:

其中:顶点域data存放顶点信息;

表头指针firstarc指向链表的第一个结点。

slide28

4

4

0

1

3

0

V1

V0

1

3

2

V2

2

V3

V4

1

2

顶点:通常按编号顺序将顶点数据存储在一维数组中;

与同一顶点关联的边:用线性链表存储

下标 编号 link

特点:设无向图中顶点数为n,边数为e,

则它的邻接表需要n个头结点和2e个表结点。

顶点Vi的度 TD(Vi)=链表i中的表结点数。

返回

slide29

图的邻接表数据类型描述:

#define MAX_VER_NUM 20

typedef struct ArcNode{

int adjvex;

struct ArcNode *next;

}ArcNode;

typedef struct VNode{

VertexType data;

EdgeNode *firstedge;

}Vnode,AdjList[MAX_VERTEX_NUM];

typedef struct{

AdjList vertices;

int vexnum,arcnum;

}ALGraph;

slide30

0

V0

V1

V2

V3

1

2

3

二、有向图邻接表

n个顶点,e条弧的有向图,需n个表头结点,e个表结点。

用链表存储同一顶点为起点的弧

在有向图中,第i个链表中的结点个数是顶点vi的出度。要求某个结点的入度,必须遍历整个邻接表。在所有链表中其邻接点域的值为i的结点的个数是顶点vi的入度。

slide31

2

V0

V1

V2

V3

3

0

0

三、有向图的逆邻接表

 为了便于确定顶点的入度或以顶点vi为头的弧,可以建立一个有向图的逆邻接表,即对每个顶点vi建立一个以vi为头的弧尾结点组成的链表。

用链表存储同一顶点为起点的弧

slide32

四、 结论

  • 对于无向图的邻接表来说,一条边对应两个单链 表结点,邻接表结点总数是边数的2倍。
  • 在无向图的邻接表中,各顶点对应的单链表的结 点数(不算表头结点)就等于该顶点的度数。
  • 在有向图邻接表中,一条弧对应一个表结点,表 结点的数目和弧的数目相同。
  • 在有向图邻接表中,单链表的结点数就等于相应 顶点的出度数。
slide33

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

  • 建立的邻接表不是唯一的,与键盘输入边的顺序 有关,输入的边的顺序不同,则得到的链表也不 同。
  • 在邻接表中容易找任一顶点的第一个邻接点和下 一个邻接点,但要判定任意两顶点之间是否有边 或弧,则需搜索第i个或第j个链表,不及邻接矩 阵方便。
slide34

7.2.3 十字链表

 十字链表是将有向图的邻接表和逆邻接表结合起来的一种有向图链式存储结构。

结点结构

有向图的每一条弧有一个弧结点,每一个顶点必有一个顶点结点,其结构为:  

弧结点

顶点结点

slide35

有向图的十字链表存储结构:

#define MAX_VERTEX_NUM 20

typedef struct ArcBox {

int tailvex,headvex;

struct ArcBox *hlink,tlink;

}ArcBox;

typedef struct VexNode {

VertexType data;

ArcBox *fisrtin, firstout;

}VexNode;

typedef struct {

VexNode xlist[MAX_VERTEX_NUM];

int vexnum,arcnum;

}OLGraph;

slide36

2

1

3

4

1 2

1 3

1

1

2

2

3

3

3 1

3 4

4

4

4 3

4 1

4 2

特点:

① 顶点结点数=顶点数 ; 弧结点数=弧的条数

② 求入度:从顶点Vi出发,沿着hlink所经过的弧结点数

求出度:从顶点Vi出发,沿着tlink所经过的弧结点数

hlink

tlink

^

^

^

^

^

^

^

^

slide37

mark

jlink

ivex

ilink

jvex

data

firstedge

7.2.4 邻接多重表

邻接多重表是无向图的另一种链式存储结构。

每一个顶点有一个顶点结点,顶点结点结构为:

图的每一条边有一个边结点,边结点的结构为:

slide38

2

1

3 2

5 2

3 5

3 4

1 4

1 2

1

1

2

2

3

3

3

4

5

4

4

5

5

^

^

^

^

^

特点:顶点结点数为n,边结点数为e;

对需要得到边的两个顶点的一类操作很方便

slide39

图的邻接多重表数据类型描述:

#define MAX_VERTEX_NUM 20

typedef emnu{ unvisited,visited} VisitIf;

typedef struct EBox{

VisitIf mark;

int ivex,jvex;

struct EBox *ilink, *jlink;

}EBox;

typedef struct VexBox{

VertexType data;

EBox *fistedge;

}VexBox;

typedef struct{

VexBox adjmulist[MAX_VERTEX_NUM];

int vexnum,edgenum;

}AMLGraph;

/*访问标记*/

/*该边依附的两个顶点的位置*/

/*指向依附这两个顶点的下一条边*/

/*指向第一条依附该顶点的边*/

/*无向图的当前顶点数和边数*/

slide40

在不同的存储结构下,实现各种操作的效率可能是不同的。所以在求解实际问题时,要根据求解问题所需操作,选择合适的存储结构。在不同的存储结构下,实现各种操作的效率可能是不同的。所以在求解实际问题时,要根据求解问题所需操作,选择合适的存储结构。

slide41
7.3 图的遍历

 和树的遍历类似,图的遍历也是从某个顶点出发,沿着某条搜索路径对图中所有顶点各作一次访问。图访问的四个难点:

首结点 、非连通图 、回路、多个相连顶点

 我们可以设置一个全局型标志数组visited来标志某个顶点是否被访过,未访问的值为0,访问过的值为1。

 根据搜索路径的方向不同,图的遍历有两种方法:深度优先搜索和广度优先搜索。 

slide42

7.3.1 深度优先搜索

  深度优先搜索遍历类似于树的先序遍历。假定给定图G的初态是所有顶点均未被访问过,在G中任选一个顶点v作为遍历的初始点,则深度优先搜索遍历可定义如下:

  • 首先访问顶点v,并将其访问标记置为访问过,即visited[v]=TRUE ;
  • 然后搜索与顶点v有边相连的下一个顶点w,若w未被访问过,则访问它,并将w的访问标记置为访问过,然后从w开始重复此过程;若w已访问,再访问与v有边相连的其它顶点;
  • 若与v有边相连的顶点都被访问过,则退回到前一个访问顶点并重复刚才过程,直到图中所有顶点都被访问完止。
  • 深度优先搜索是一种递归的过程
slide43

V0

V7

V6

V5

V4

V3

V2

V1

深度优先遍历

从图中某顶点v出发:

  • 访问顶点v;
  • 依次从v的未被访问的邻接点出发,对图进行深度优先遍历;

先序遍历(DLR)

若二叉树非空

如果将图顶点的邻接点看作二叉树结点的左、右孩子深度优先遍历与先序遍历是不是很类似?

  • 访问根结点;
  • 先序遍历左子树;
  • 先序遍历右子树;
slide44

V1

V2

V3

V4

V5

V6

V7

V8

V1

V2

V3

V4

V5

V6

V7

V8

深度遍历:V1 V2 V4  V8 V5 V6 V3 V7

由于没有规定访问邻接点的顺序,深度优先序列不是唯一的

深度遍历:V1 V2 V4  V8 V3 V6 V7 V5

slide45

对某顶点的深度优先遍历的递归算法:

Boolean visited[MAX];

void DFSTraverse(Graph G,int v){

for(v=0;v<G.vexnum;++v)

visited[v]=FALSE;

for(v=0;v<G.vexnum;++v)

if(!visited[v])

DFS(G,v);

}

//访问标志数组

//标志数组初始化

//依次对数组中的每个未访问的顶点调用DFS搜索

slide46

以v为起点对G进行DFS搜索

void DFS (Graph G,int v){

visited[v]=TRUE;

for(w=FirstAdjVex(G,v);w>=0; w=NextAdjVex(G,v,w))

if (!visited[w])

DFS (G,w);

}

//访问顶点v

//对v的尚未访问过的邻

接点w递归调用DFS

slide47

V0

V1

V2

V3

V4

V5

V6

V7

0

1

2

3

4

5

6

7

4

0

7

1

2

3

0

5

6

1

6

7

3

2

2

4

1

5

V1

V7

V6

V5

V2

V3

V4

V0

深度遍历:V0

V1 

V3 

V7 

V4 

V2 

V5 

V6

V0

V1

V2

V3

V4

V5

V6

V7

slide48

深度优先搜索的算法复杂度:

 在遍历时,对图中每个顶点至多调用一次DFS 函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。因此,遍历图的过程实质上是对每个顶点查找其邻接点的过程。其耗费的时间则取决于所采用的存储结构。 

slide49

0

1

2

3

 当用二维数组表示邻接矩阵图的存储结构时,查找每个顶点的邻接点所需时间为O(n2),其中n为图中顶点数。

 而当以邻接表作图的存储结构时,找邻接点所需时间为O(e),其中e为无向图中边的数或有向图中弧的数。由此,当以邻接表作存储结构时,深度优先搜索遍历图的时间复杂度为O(n+e)。

slide50

V1

V2

V3

V4

V5

V6

V7

V8

7.3.2 广度优先搜索

  广度优先搜索遍历类似于树的按层次遍历。设图G的初态是所有顶点均未访问,在G 中任选一顶点v作为初始点,则广度优先搜索的基本思想是:

  • 首先访问顶点v,并将其访问标志置为已被访问,即visited[v]=TRUE ;
  • 接着依次访问与顶点v有边相连的所有顶点W1,W2,…,Wt;
  • 然后再按顺序访问与W1,W2,…,Wt有边相连又未曾访问过的顶点;依此类推,直到图中所有顶点都被访问完为止 。

 即广度优先搜索遍历图的过程中以v为起始点,由近至远,依次访问和v有路径相通且路径长度为1,2,…的顶点。

slide51

V1

V4

V5

V6

V7

V2

V3

V0

V1

V7

V6

V5

V2

V3

V0

V4

求图G 的以V0起点的的广度优先序列:

V0,V1,V2,V3,V4,V5,V6,V7

在遍历过程中

所经过的路径

由于没有规定访问邻接点的顺序,广度优先序列不是唯一的

slide52

V0

V7

V6

V5

V4

V3

V2

V1

广度优先遍历算法(类似于树的按层遍历)

  • 从图中某顶点v出发:
  • 访问顶点v ;(容易实现)
  • 访问v的所有未被访问的邻接点w1,w2,…wk;(容易实现)
  • 依次从这些邻接点(在步骤2)访问的顶点)出发,访问它们的所有未被访问的邻接点; 依此类推,直到图中所有访问过的顶点的邻接点都被访问;
  • 为实现 3),需要保存在步骤2)中访问的顶点,而且访问这些顶点邻接点的顺序为:先保存的顶点,其邻接点先被访问。

在广度优先遍历算法中,需设置一队列Q,保存已访问的顶点,并控制遍历顶点的顺序。

slide53

V1

V2

V3

V4

V5

V6

V7

V8

V1

V2

V3

V4

V5

V6

V7

V8

广度遍历:V1 V2 V3  V4 V5 V6 V7 V8

广度遍历:V1 V2 V3  V4 V6 V7 V8 V5

slide54

开始

访问v,置标志

初始化队列

v入队

Y

队列空吗

a

N

访问w,置标志

队头v出队

结束

求v邻接点w

w入队

N

w存在吗

v下一邻接点w

Y

Y

w访问过

N

a

广度优先

搜索算法

slide55

void BFSTraverse(Graph G,int v){

for(v=0;v<G.vexnum;++v) visited[v]=FALSE;

InitQueue(Q);

for(v=0;v<G.vexnum;++v)

if(!visited[v]){

visited[v]=TRUE;

EnQueue(Q,v);

while(!QueueEmpty(Q)){

DeQueue(Q,u);

for (w=FisrtAdjVex(G,u);w>=0;

w=NextAdjVex(G,u,w))

if(!visited[w]){

visit(w);EnQueue(Q,w);

}

}

}

}

v尚未访问

v入队列

队头元素出队并置为u

w为u的尚未访问的邻接点

slide56

V0

vextex

firstedge

adjvex

next

^

0

v0

V1

V2

V3

v1

1

^

V4

2

v2

^

4

3

4

4

0

0

2

0

3

1

1

2

3

v3

^

4

v4

^

广度遍历:V0

V3 

V2 

V1 

V4

slide57

f

f

v3

v3 v2

1 2 3 4 5 6

1 2 3 4 5 6

f

r

r

v0

遍历序列:v0 v3

遍历序列:v0 v3 v2

1 2 3 4 5 6

f

f

f

r

v3 v2 v1

遍历序列:v0

v2 v1

v2 v1 v4

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

r

r

r

遍历序列:v0 v3 v2 v1

遍历序列:v0 v3 v2 v1

序列:v0 v3 v2 v1 v4

f

f

f

v1 v4

v4

1 2 3 4 5 6

1 2 3 4 5 6

1 2 3 4 5 6

r

r

r

序列:v0 v3 v2 v1 v4

序列:v0 v3 v2 v1 v4

序列:v0 v3 v2 v1 v4

程序

结构

slide58

V0

V4

V3

V0

V2

V3

V5

V4

V7

V6

V2

V1

V1

7.4 图的连通性问题

 在对无向图进行遍历时,对于连通图,仅需从图中任一顶点出发,进行深度优先搜索或广度优先搜索,便可访问到图中所有顶点。

 对非连通图,则需从多个顶点出发进行搜索,而每一次从一个新的起始点出发进行搜索过程中得到的顶点访问序列恰为其各个连通分量中的顶点集。

slide59

B

A

C

D

E

F

0

A

^

4

1

2

5

3

5

1

4

0

B

1

0

^

2

^

C

3

D

^

4

E

^

5

F

^

7.4.1 无向图的连通分量和生成树

 对上图采用邻接表存储,进行深度优先搜索遍历,需调用两次DFS(即分别从顶点A 和C出发),得到的顶点访问序列分别为:

A B F E C D

slide60

 这两个顶点集分别加上所有依附于这些顶点的边,便构成了非连通图的两个连通分量。 这两个顶点集分别加上所有依附于这些顶点的边,便构成了非连通图的两个连通分量。

void Traver (Graph G){

for (v=0;v<G.vexnum;++v) visited[v]=FALSE;

for (v=0;v<G.vexnum;++v)

if (!visited[v]) DFS (G,v);

}

{ Count++; }

 要想判定一个无向图是否为连通图,或有几个连通分量,就可设一个计数变量count,初始时取值为0,在深度优先算法的第二个for循环中,每调用一次DFS,就给count增1。当整个算法结束时,依据count的值,就可确定图的连通性了。

slide61

生成树和生成森林

生成树:在一个无向连通图G中,如果取它的全部顶点和一部分边构成一个子图G`,若边集E(G`)中的边刚好将图的所有顶点连通但又不形成环路,我们就称子图G`是原图G的生成树。  

生成森林:若一个图是非连通图,但有若干个连通分量,则通过深度优先搜索遍历或广度优先搜索遍历,不可以得到生成树,但可以得到生成森林,且若非连通图有n个顶点,m个连通分量,则可以遍历得到m棵生成树,合起来为生成森林,森林中包含n-m条树边。  

slide62

V0

V1

V2

V3

V4

V5

V6

V7

 设E(G)为连通图G中所有边的集合,则从图中任一顶点出发遍历图时,必定将E(G)分成两个集合T(G)和B(G),其中T(G)是遍历图过程中历经的边的集合;B(G)是剩余的边的集合。可以证明T(G)和图G中所有顶点一起构成连通图G的极小连通子图。

 若G是连通图则它是G的一棵生成树,并且由深度优先搜索得到的为深度优先生成树;由广度优先搜索得到的为广度优先生成树。 若G是非连通图,通过这样的遍历,将得到的是生成森林。  

slide63

V0

V0

V0

V1

V1

V1

V2

V2

V2

V3

V3

V3

V4

V4

V4

V5

V5

V5

V6

V6

V6

V0

V7

V7

V7

V0

V1

V2

V1

V2

V3

V5

V3

V4

V5

V6

V7

V6

V7

V4

广度优先生成树

深度优先生成树

深度遍历:V0 V1 V3  V7 V4 V2 V5 V6

广度遍历:V0 V1 V2  V3 V4 V5 V6 V7

slide64

A

B

C

D

E

F

G

H

I

K

J

G

D

A

L

M

K

I

E

L

C

F

H

M

B

J

深度优先生成森林

slide65

如何求深度优先生成树与广度优先生成树?

 要求得生成树,可考虑用深度优先搜索遍历及广度优先搜索遍历算法。

 对于深度优先搜索算法DFS,由DFS(i)递归到DFS(j),中间必经过一条边(i,j),因此,只需在DFS(j)调用前输出这条边,即可求得生成树的一条边,整个递归完成后,则可求得生成树的所有边。

生成森林的构造?

 假设以孩子兄弟链表作生成森林的存储结构,则算法A生成非连通图的深度优先生成森林,其中DFSTree函数如算法B所示。算法A的时间复杂度和遍历相同。  

slide66

void DESForest(Graph G,CSTree &T) {

T=NULL;

for (v=0;v<G.vexnum;++v)

visited[v]=FALSE;

for(v=0;v<G.vexnum;++v)

if (!visited[v]){

p=(CSTree)malloc(sixeof(CSNode));

*p={GetVex(G,v).NULL,NULL};

if (!T) T=p;

else q->nextsibling=p;

q=p;

DFSTree(G,v,p);

}

}

算法A:

顶点v为新的生成树的根结点

分配根结点

给根结点赋值

树根之间为兄弟关系

建立以p为根的生成树

slide67

算法B:

从顶点v出发深度优先遍历图G,建立以T为根的生成树

void DFSTree(Graph G,int v,CSTree &T){

visited[v]=TRUE; first=TRUE;

for(w=FirstAdjVex(G,v);w>=0;w=NextAdjVex(G,v,w))

if(!visited[w]){

p=(CSTree)malloc(sizeof)CSNode));

*p=(GetVex(G,w),NULL,NULL);

if (first){

T->lchild=p; first=FALSE;

}elseq->nextsibling=p;

q=p;

DFSTree(G,w,q);

}

}

分配孩子结点

w是v的第一个邻接点,则作为v的左孩子结点

w是v的其它邻接顶点,作为上一邻接点的右兄弟

建立w的生成子树

slide68

无向图的连通分量

  • 说明:
    • 一个图可以有许多棵不同的生成树
    • 所有生成树具有以下共同特点:
      • 生成树的顶点个数与图的顶点个数相同
      • 生成树是图的极小连通子图
      • 一个有n个顶点的连通图的生成树有n-1条边
      • 生成树中任意两个顶点间的路径是唯一的
      • 在生成树中再加一条边必然形成回路
    • 含n个顶点n-1条边的图不一定是生成树
slide69

7

7

5

9

13

24

10

17

12

18

1

2

6

5

3

4

7.4.3 最小生成树

1、问题提出

要在n个城市间建立通信联络网

顶点——表示城市

权——城市间建立通信线路所需花费代价

希望找到一棵生成树,它的每条边上的权值之和(即建立该通信网所需花费的总代价)最小———最小代价生成树

slide70

7

7

5

9

13

24

10

17

12

18

1

2

6

5

3

4

最小生成树

2、问题分析

n个城市间,最多可设置n(n-1)/2条线路;

n个城市间建立通信网,只需n-1条线路;

问题转化为:如何在可能的线路中选择n-1条,能把 所有城市(顶点)均连起来,且总耗费(各边权值之和)最小。

3、定义

对于带权的连通图G,其生成树也是带权的。我们把生成树各边的权值总和称为该生成树的权。并且将权最小的生成树称为最小生成树。

slide71

V1

V1

V1

5

6

1

1

1

V2

V4

5

5

V3

V3

V3

3

2

4

4

6

6

V6

V5

V6

V1

V1

V1

1

1

1

V4

V2

V4

V2

V4

5

5

V3

V3

V3

2

3

2

2

4

4

4

V6

V5

V6

V6

Prim方法构造过程

V1

slide72

普里姆(Prim)算法

Prim算法可用下述过程描述:

(1)U={u1},T={ };

(2)while (U≠V)do

 (u,v)=min{wuv;u∈U,v∈V-U }

T=T+{(u,v)}

U=U+{v}

(3)结束。

slide73

V1

5

6

1

V2

V4

5

5

V3

3

2

4

6

6

V5

V6

辅助数组各分量值的变化:

有关数据的存储结构

设置一个辅助数组closedge, 它包括两个域:

lowcost用来保存集合V-U中各顶点与集合U中各顶点构成的边中具有最小权值的边的权值;

adjvex用来保存依附于该边

的在集合U中的顶点。  

slide74

普里姆(Prim)算法

  • void Prim(Mgraph G,VertexType u){
  • k=LocateVex(G,u);
  • for(j=0;k<G.vexnum;++j)
  • if(j!=k) closedge[j]={u,G.arcs[k][j]};
  • closedge[k].lowcost=0;
  • for (i=1;i<G.vexnum;++i){
  • k=minmum(closedge);
  • printf(closedge[k].adjvex,G.vexs[k]);
  • closedge[k].lowcost=0;
  • for (j=0;j<G.vexnum;++j)
  • if (G.arcs[k][j]<colsedge[j].lowcost)
      • closedge[j]={G.vexs[k],G.arcs[k][j]};
  • }
  • }

辅助数组初始化

初始U={u}

寻找当前最小权值

第k个顶点归并到集合U

新顶点并入U后重新选择最小边

slide75

1

5

6

1

2

4

5

5

3

3

2

4

6

6

5

6

克鲁斯卡尔(Kruskal)算法

  • 初始条件:假设G = (V,E) 是一个具有n个顶点的连通网络, G`=(U,T)是G 的最小生成树,U的初值等于V,即包含有G中的全部顶点,T的初值为空集。
  • 基本思想:将图G中的边按权值从小到大的顺序依次选取,若选取的边使生成树G`不形成环路,则把它并入T中,保留作为生成树G`的一条边,若选取的边使生成树G`形成环路,则将其舍弃,如此进行下去,直到T中包含n-1条边为止。此时的T即为最小生成树。
slide76

A

D

E

3

F

G

2

4

B

C

8

9

4

1

9

7

5

8

6

1

作业:

1) 根据普里姆算法思想,画出构造该无向带权图最小生成树的过程。

2) 根据克鲁斯卡尔算法思想,画出构造该无向带权图最小生成树的过程。

slide77

7.5 有向无环图及其应用

一个无环的有向图称做有向无环图,简称DAG图。下面是有向树、DAG图和有向图的例子。

有向树 DAG图 有向图

slide78

有向无环图

 有向无环图可以用来描述含有公共子式的表达式。例如下述表达式:

((a+b)×(b×(c+d))+(c+d)×e)×((c+d)×e)

二叉树描述表达式 有向无环图描述

slide79

有向无环图的应用

有向无环图也是描述一项工程或系统的进行过程的有效工具。

对整个工程和系统,人们关心两个方面的问题:

一是工程能否顺利进行:

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

 下面我们通过对有向图进行拓扑排序和关键路径操作来解决这两个问题。

slide80

7.5.1 拓扑排序

 在工程实践中,一个工程项目往往由若干个子项目组成,这些子项目间往往有多种关系:

①先后关系,即必须在一子项目完成后,才能开始实施另一个子项目;

②子项目之间无次序要求,即两个子项目可以同时进行,互不影响。

 我们把这些子项目、工序、课程看成一个个顶点称之为活动。这种用顶点表示活动、弧表示活动间优先关系的有向无环图称为顶点表示活动的网(Activity On Vertex network ),简称AOV网。

slide81

V0

V3

V1

V2

 在AOV网中,如果从顶点Vi到Vj之间存在有向边

< Vi, Vj>,则表示活动i必须先于活动j进行。如果顶点Vi到顶点Vj存在一条有向路径时,称Vi为Vj的前趋,Vj为Vi的后继。这种前趋后继关系有传递性。

 如下图存在环路,则表示顶点之间的先后关系进入了死循环。如果用AOV网描述的是一项工程,那么这个AOV网一定不能存在环。

 因此,对给定的AOV网络首先要判定网络中是否存在环路。

slide82

C4

C5

课程编号 课程名称 先决条件

C1

C2

C3

C4

C5

C6

C7

C8

C9

C10

C11

C12

程序设计

离散数学

数据结构

汇编语言

算法分析

计算机原理

编译方法

操作系统

高等数学

线性代数

电子电路

数值分析

C1

C1,C2

C1

C3,C4

C11

C5,C3

C3,C6

C9

C9

C9,C10,C1

C2

C1

C3

C7

C12

C10

C9

C8

C6

C11

课程流程图

slide83

C4

C5

C2

C1

C3

C7

C12

C10

C9

C8

C6

C11

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

 由于AOV网络中有些顶点之间没有次序要求,它们在拓扑有序序列中的位置可以任意颠倒,所以拓扑排序的结果一般并不是唯一的。通过拓扑排序还可以判断出此AOV网络是否包含有有向环路,若有向图G所有顶点都在拓扑排序序列中,则AOV网络必定不包含有有向环路。

 如上例,可以得到不止一个拓扑排序:

C1-C2-C3-C4-C5-C7-C9-C10-C11-C6-C12-C8

C9-C10-C11-C6-C1-C12-C4-C2-C3-C5-C7-C8

 显然,对于任何一项工程中各个活动的安排,必须按拓扑有序序列中的顺序进行才是可行的。

slide84

拓扑排序的步骤

对于给定的AOV网拓扑排序的步骤:

任选一个入度为0的顶点输出;

删除此顶点和以它为出发点的所有弧;

重复执行上述两步,直至图中无入度为0的顶点;

若输出顶点数小于图中顶点数,则说明图中存在环,无法产生其拓扑排序,否则输出的顶点序列即为此图的一个拓扑排序。

slide85

V1

V2

V2

V2

输出V5

输出V3

输出V6

输出V4

输出V1

V4

V3

V4

V3

V3

V6

V5

V6

V5

V6

V5

V2

V2

V3

V5

V5

AOV网上的输出

V2

拓扑序列为:

V1,V4,V6,V3,V5,V2

slide86

0

1

V1

V2

2

3

V4

V3

4

5

3

1

4

4

4

2

1

3

data

firstarc

adjvex

nextarc

V6

V5

^

v1

^

v2

^

v3

v4

^

^

v5

^

v6

拓扑排序算法实现

slide87

Status Topo_Sort (AlGraph G){

FindInDegree(G,indegree);

InitStack(S);

for (i=0;i<G.vexnum;++i)

if (!indegree[i]) Push(S,i);

count=0;

while(!StackEmpty(S)){

Pop(S,i);

printf(i,G->vertices[i].data);

++count;

for(p=G.vertices[i].firstarc;p;p=p->nextarc){

k=p->adjvex;

if(!(--indegree[k])) Push(S,k);

}

}

if(count<G.vexnum) return ERROR;

else return OK;

}

slide88

void FindInDegree(ALGraph G,int indegree[MAX]);

for (i=0;i<G.vexnum;++i)

indegree[i]=0;

for (i=0;i<G.vexnum;++i){

p=G.vertices[i].firstarc;

while(p){

indegree[p->adjvex]++;

p=p->nextarc;

}

}

}

初始化

找以顶点i为起点的第一条弧

将找到的以顶点i为起点的弧的入度加1

找以顶点i为起点的下一条弧

slide89

7.5.2 关键路径

1、什么是AOE网?

如果在无环的带权有向图中, 用有向边表示一个工程中的活动,用边上权值表示活动持续时间, 用顶点表示事件(每个事件表示在它之前的活动已完成,在它之后的活动可以开始),则这样的有向图叫做用边表示活动的网络, 简称 AOE (Activity On Edges)网络。

AOE网络在某些工程估算方面非常有用。

slide90

a10=2

a4=1

a7=9

a1=6

a2=4

a8=7

a11=4

a5=1

a3=5

a9=4

a6=2

V7

V2

V9

V1

V5

V8

V3

V4

V6

例 设一个工程有11项活动,9个事件

事件V1——表示整个工程开始

事件V9——表示整个工程结束

整个工程只有一个开始点和一个完成点,在正常情况下,网中只一个入度为零的点(称为源点)和一个出度为零的点(称为汇点)。

问题: (1) 完成整项工程至少需要多少时间?

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

slide91

2

a4=2

a1=6

a2=4

1

4

a5=1

3

  • 2、定义
  • AOE网是一个带权的有向无环图,其中顶点表示事件,弧表示活动,权表示活动持续时间。
  • 路径长度:路径上各活动持续时间之和。
  • 关键路径:路径长度最长的路径叫关键路径。
  • Ve(j):表示事件Vj的最早发生时间
  • Vl(i):表示事件Vi的最迟发生时间
  • e(k):表示活动ak的最早开始时间
  • l(k):表示活动ak的最迟开始时间
  • l(k)-e(k):表示完成活动ak的时间余量。
  • 关键活动:关键路径上的活动,即l(k)=e(k)的活动。
slide92

k

i

j

3、问题分析

如何找e(k)=l(k)的关键活动?

 设活动ak用弧<i,j>表示,其持续时间记为:dut(<i,j>)

则有:(1)e(k)=Ve(i)

(2)l(k)=Vl(j)-dut(<i,j>)

(1) 从Ve(0)=0向后递推,计算每一事件Vj的最早发生时间:

(2) 从Vl(n-1)=Ve(n-1)开始,计算每个事件的最迟发生时间:

slide93

k

i

j

活动ak的最早开始时间e(k):如果活动ak所对应的弧是<i,j>,则e(k)等于从源点V0到顶点Vi的最长路径的长度,即: e(k)=Ve(i)。

活动ak的最迟开始时间l(k):在保证事件Vj的最迟发生时间为Vl(j)的前提下,活动ak最迟须开始的时间;如果活动ak所对应的弧是<i,j>,活动ak的持续时间为dut(<i,j>) ,那么l(k)=Vl(j)-dut(<i,j>)。

活动ak的时间余量s(k):它等于活动ak的最迟开始时间l(k)与最早开始时间e(k)之差。关键活动是那些时间余量为0的活动。

slide94

a10=2

a4=1

a7=9

a1=6

a2=4

a8=7

a11=4

a5=1

a3=5

a9=4

a6=2

活动 e(k) l (k) l(k)-e(k)

7

0

16

6

4

0

5

7

7

0

14

6

14

3

0

8

2

6

7

16

7

10

2

0

2

0

3

0

0

3

0

3

0

顶点 Ve(j) Vl(i)

V7

V2

V9

V1

V5

V8

V3

V4

V6

4、求关键路径步骤

  • 求Ve(j)
  • 求Vl(i)
  • 求e(k)
  • 求l(k)
  • 计算l(k)-e(k)

a1

a2

a3

a4

a5

a6

a7

a8

a9

a10

a11

V1

V2

V3

V4

V5

V6

V7

V8

V9

0

6

4

5

7

7

16

14

18

0

6

6

8

7

10

16

14

18

slide95

Status Topo_Order(AlGraph G,Stack &T){

FindInDegree(G,indegree); InitStack(S);

InitStack(T); count=0; ve[0..G.vexnum-1]=0

for (i=0;i<G.vexnum;++i)

if (!indegree[i]) Push(S,i);

while(!StackEmpty(S)){

Pop(S,j); Push(T,j);

++count;

for(p=G.vertices[j].firstarc;p;p=p->nextarc){

k=p->adjvex;

if(!(--indegree[k])) Push(S,k);

if(ve[j]+p->weight>ve[k])

ve[k]=ve[j]+p->weight;

}

}

if(count<G.vexnum) return ERROR;

else return OK;

}

slide96

Status CriticalPath(AlGraph G){

if(!Topo_Order(G,T)) return ERROR;

vl[G.vexnum-1]= ve[G.vexnum-1];

while(!StackEmpty(T))

for(Pop(T,j), p=G.vertices[j].firstarc; p;p=p->nextarc){

k=p->adjvex; dut=p->weight;

if(vl[k]-dut<vl[j]) vl[j]=vl[k]-dut;

}

for(j=0;j<G.vexnum;++j)

for(p=G.vertices[j].firstarc;p;p=p->nextarc){

k=p->adjvex; dut=p->weight;

ee=ve[j];el=vl[k]-dut;

tag=(ee==el)?`*`:``;

printf(j,k,dut,ee,el,tag);

}

}

slide97
关键路径上的活动都是关键活动;

缩短非关键活动都不能缩短整个工期;

分析关键路径的目的是找出影响整个工期的关键活动,缩短关键活动的时间,常可以缩短整个工期;

在有多条关键路径时,缩短那些在所有关键路径上的关键活动,才能缩短整个工期。

a10=2

7

2

a4=1

a7=9

a1=6

9

a2=4

a8=7

1

a5=1

5

a11=4

8

a3=5

3

a9=4

a6=2

4

6

5、结论:

slide98

7.6 最短路径

问题提出

用带权的有向图表示一个交通运输网,图中:

顶点——表示城市

边——表示城市间的交通联系

权——表示此线路的长度

问题:如何找到城市A到城市B之间一条距离最近的通路?

  数学模型:从某顶点出发,沿图的边到达另一顶点所经过的路径中,各边上权值之和最小的一条路径——最短路径

slide99

v1

长度

最短路径

0

v2

32

13

<V0,V1>

8

1

8

13

<V0,V2>

2

源点

13

<V0,V2,V3>

7

6

9

5

30

19

<V0,V2,V3,V4>

3

21

<V0,V2,V3,V4,V5>

5

17

6

2

20

<V0,V1,V6>

4

7.6.1 从某个源点到其余各顶点的最短路径

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

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

slide100

迪杰斯特拉算法

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

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

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

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

slide101

迪杰斯特拉算法

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

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

其余最短路径的特点:

它或者是直接从源点到该点(只含一条弧); 或者是从源点经过已求得最短路径的顶点,再到达该顶点。

slide102

迪杰斯特拉算法

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

设置辅助数组Dist,其中每个分量Dist[k] 表示 当前所求得的从源点到其余各顶点 k 的最短路径。

  • 一般情况下,
  • Dist[k] = <源点到顶点 k 的弧上的权值>
      • 或者 = <源点到其它顶点的路径长度>
  • + <其它顶点到顶点 k 的弧上的权值>。
slide103

迪杰斯特拉算法

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

V0和Vi之间存在弧

V0和Vi之间不存在弧

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

2)修改其它各顶点的Dist[k]值。

假设求得最短路径的顶点为w,

若 Dist[w]+G.arcs[w][k]<Dist[k]

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

slide104

0

32

8

1

13

2

6

7

30

9

5

3

5

17

6

2

4

迪杰斯特拉算法

终点 从V0到各终点的最短路径及其长度

-------

-------

-------

19

<V0,V2,V3,V4>

22

<V0,V1,V5>

20

<V0,V1,V6>

V4:19

<V0,V2,V3,V4>

13

<V0,V1>

-------

13

<V0,V2,V3>

30

<V0,V4>

32

<V0,V6>

V1:13

<V0,V1>

-------

-------

13

<V0,V2,V3>

30

<V0,V4>

22

<V0,V1,V5>

20

<V0,V1,V6>

V3:13

<V0,V2,V3>

--------

--------

--------

--------

21

<V0,V2,V3,V4,V5>

20

<V0,V1,V6>

V6:20

<V0, V1,V6>

13

<V0,V1>

8

<V0,V2>

30

<V0,V4>

32

<V0,V6>

V2:8

<V0,V2>

V1

V2

V3

V4

V5

V6

Vj

slide105

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

顶点对之间的最短路径概念

所有顶点对之间的最短路径是指:对于给定的有向网G=(V,E),要对G中任意一对顶点有序对u、v(u≠v),找出u到v的最短距离和v到u的最短距离。

解决此问题的一个有效方法是:轮流以每一个顶点为源点,重复执行迪杰斯特拉算法n次,即可求得每一对顶点之间的最短路径,总的时间复杂度为O(n3)。

下面将介绍用弗洛伊德(Floyd)算法来实现此功能,时间复杂度仍为O(n3),但该方法比调用n次迪杰斯特拉方法更直观一些。

slide106

顶点对之间的最短路径概念

弗洛伊德算法使用前面定义的图的邻接矩阵G.arcs[N][N]来存储带权有向图。算法的基本思想是:

设置一个N*N的矩阵D[N][N],其中除对角线的元素都等于0外,其它元素D[i][j]的值表示顶点i到顶点j的最短路径长度,运算步骤为:

开始时,以任意两个顶点之间的有向边的权值作为路径长度,没有有向边时,路径长度为∞,此时, D[i][j]=G.arcs[i][j],

以后逐步尝试在原路径中加入其它顶点作为中间顶点,如果增加中间顶点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径,修改矩阵元素。具体做法为:

slide107

弗洛伊德算法

第一步,让所有边上加入中间顶点0,取D[i][j]与D[i][0]+D[0][j]中较小的值作D[i][j]的值.

第二步,让所有边上加入中间顶点1,取D[i][j]与D[i][1]+D[1][j]中较小的值,完成后得到D[i][j] …,如此进行下去,当第n步完成后,得到 D[i][j] ,即为我们所求结果, D[i][j]表示顶点i到顶点j的最短距离。

因此,弗洛伊德算法可以描述为: 

D(-1)[i][j]=G.arcs[i][j]; //G.arcs为图的邻接矩阵 

D(k)[i][j]=min{D(k-1) [i][j], D(k-1) [i][k]+D(k-1) [k][j]}

其中 k=0,1,2,…, n-1

slide108

6

A

B

4

11

3

2

C

弗洛伊德算法

slide109

25

V2

V5

10

16

7

V1

V4

12

3

10

12

2

V3

V6

8

4

作业

1)对下图所示的有向带权图,根据迪杰斯特拉算法,画出生成从结点v1到其余各结点最短路径的过程。

ad