第6章 图 6.2图的存储结构 6.3图的遍历 6.4图的连通性 6.5 最短路径 6.6 AOV网与拓扑排序 6. 7 AOE网与关键路径

Slides:



Advertisements
Similar presentations
26个英语字母 let's go!.
Advertisements

第16课时: 桥.
计算机三级考试C语言上机试题专题.
第四章 运筹学模型 本章重点: 线性规划基础模型、目标规划模型、运输模型 及其应用、图论模型、最小树问题、最短路问题.
第十五章 欧拉图与哈密顿图 主要内容 欧拉图 哈密顿图 带权图与货郎担问题.
本命年的回想 刘绍棠.
第7章 图论 7.1 图的基本概念 7.6 树 7.2 路与圈 7.7 根树及其应用 7.3 图的矩阵表示 7.8 偶图与匹配
教学目标 分析大堰河的形象、情感,解读诗人的歌唱; 把握抒情诗的记事、写人,探知作品的特色。 学法指引 学习真话、真情的写作表达。 重点探究
贪婪算法.
数据结构与算法(B) 期中后MOOC课程小测
图论算法 ---最大流问题 长沙市雅礼中学 朱 全 民.
计算机硕士专业基础—C语言 赵海英
Chapter 12 圖形結構 12.1 圖形的一些專有名詞 12.2 圖形資料結構表示法 12.3 圖形追蹤 12.4 擴展樹
离 散 数 学 计算机科学与工程学院 示 范 性 软 件 学 院 电子科技大学 2017年3月21日星期二.
Chapter 07 Graph 第七章 图 Prof. Qing Wang.
数据结构算法 模拟练习及解答二 绍兴文理学院 计算机系计算机应用教研室.
C语言基础——指针的高级应用 Week 05.
第7章 图论基础与应用 PART A 《可视化计算》.
《战国策》:范雎说秦王学习要点 一、《战国策》题解 二、长沙马王堆汉墓简介 三、《范雎说秦王》说明 四、《范雎说秦王》语言角度分析
1.图的基本概念 2.树 3.最短路 4.最大流问题 5.最小费用最大流 6.中国邮递员问题
第八章 第一节 日本 邹旭丹 滨河中学初中部 湘教版地理初一年级.
7.5 擴張樹 (Spanning Tree) 一個無向相連圖 G 的子圖 G’ 如果可用最少的邊來連接G 的
第3章 栈和队列 3.1 栈 3.2 队列 3.3 应用.
第4章 串 串的基本概念和C语言的串函数 串的存储结构 动态数组实现的顺序串 串的模式匹配算法——BF算法 主要知识点.
第3章 算法与数据结构_c语言描述 3.1 计算机的问题求解模型 3.2 C语言基础 3.3 算法与数据结构的基本概念 3.4 线性结构
第五章 图论 5.6平面图 例 考虑把三座房和三种 设施每种都连接起来的问题, 如图7-64所示,是否有可能使 得这样的连接里不发生交叉?
第8章 圖形結構(Graphs) 8-1 圖形的基本觀念 8-2 圖形的表示法 8-3 圖形的走訪 8-4 擴張樹
第八章 图 图的基本概念 图的存储结构 图的遍历与连通性 最小生成树 最短路径 活动网络.
复习.
线性表小结 元素之间的线性关系 顺序表 顺序表:元素相邻存储 单链表:后继指针链接 一维数组 给定下标随机存取
第2章 线性表 线性表抽象数据类型 顺序表 主要知识点 单链表 循环单链表 循环双向链表 静态链表 设计举例.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
Chapter 7 圖形與網路 資料結構導論 - C語言實作.
第3章 堆栈和队列 堆栈 堆栈应用 队列 队列应用 优先级队列 主要知识点.
第3章 栈和队列(一).
第16讲 图的矩阵表示, 赋权图与最短路径 主要内容: 1.图的矩阵表示. 2.赋权图与最短路径..
§7.4.2 最小生成树( Minimum Spanning Tree)
第9章 内部排序 9.1 概述 9.2 插入排序 9.3 快速排序 9.4 选择排序 9.5 归并排序 9.6 基数排序
离散数学─图论初步 南京大学计算机科学与技术系
資料結構 第4章 堆疊.
运筹学 图与网络分析 1.
3.1 线性表及逻辑结构 3.2 线性表的顺序存储 3.3 线性表的链式存储 3.4 链式存储结构的应用
第四章 第三节 最短路径算法.
数据结构概论 第7章 图 董黎刚 浙江工商大学信电学院 2019年4月8日.
第6章 数组与广义表 6.1 数组的定义及其基本操作 6.2 数组的顺序存储结构 6.3 矩阵的压缩存储 6.4 广义表的概念
算法设计复习 内容提要: 算法设计思想回顾(递归和分治、动态规划、贪心算法、回溯法、分支限界法) 经典例子讲解 2019/4/9.
第四章 向量处理机 银河-I巨型计算机 银河-II巨型计算机
§1 图与网络的基本概念 §2 树图与最小生成树 §3 最短路问题 §4 最大流问题 §5 最小费用最大流问题
定义7.5:设图G的顶点非空真子集为V1V, 在G中一个端点在V1中, 另一端点在V(G)-V1中的所有边组成的集合称为G的一个断集或称边割,记为 E(V1(V(G)-V1)), 简记为(V1, V(G)-V1)。 当|(V1, V(G)-V1)|=1时, (V1, V(G)-V1)中的那条边称为割边或.
Main() { Dfas Asdfasf fasdfa } #include <stdio.h> void main( ) {
第 五 章 图 论 5.3 树 1. 无向树 2. 有向树 3. 周游算法 4. 前缀码与最优树.
请编写程序在屏幕上打印出一个“*”? printf(”*\n”); 请编写程序在屏幕上打印四行,每行一个“*”?
第2讲 绪论(二).
第1章 绪论(二) 教学目标 理解算法的特性及评价标准 掌握算法时间复杂度和空间复杂度的分析方法 1/
第8章 图 8.1 图的基本概念和基本操作 8.2 图的邻接矩阵存储结构 8.3 图的邻接表存储结构 8.4 图的其他存储结构
图(二).
鄧姚文 資料結構 第八章:圖(Graph) 鄧姚文
网络模型 Network Modeling Operations Research 运 筹 学
樂理教學                 茄苳國小蔡逸凡老師.
离散数学─图论初步 南京大学计算机科学与技术系
图练习.
本节内容 指针类型.
4.7 关键路径算法 源点 起始点 v1 v4 v3 v2 v5 v6 v7 v8 v9 a1=6 a6=2 a3=5 a2=4 a5=1
第六章 图.
圖形結構 Graph Structure chapter 9 德明科技大學資訊科技系.
第四部分 图论 主要内容 图的基本概念:图、通路和回路、图的连通性、图的矩阵表示 图的可行遍性:欧拉图、哈密顿图、带权图及其应用
第7章 图.
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C语言基础学习 从外行到入门.
第十三章 几种特殊的图 主要内容 欧拉图 哈密顿图 二部图与匹配 平面图 着色.
Presentation transcript:

第6章 图 6.2图的存储结构 6.3图的遍历 6.4图的连通性 6.5 最短路径 6.6 AOV网与拓扑排序 6. 7 AOE网与关键路径 第6章 图 6.1图的基本概念 6.2图的存储结构 6.3图的遍历 6.4图的连通性 6.5 最短路径 6.6 AOV网与拓扑排序 6. 7 AOE网与关键路径

6.1图的基本概念 6.1.1图的定义 图G是由两个集合V和E组成,V是有限的非空顶点集,E是顶点对所构成的边集,分别用V(G)和E(G)表示。用二元组G=(V,E)表示图G。

G1=(V1,E1) V1={A,B,C,D,E} E1={(A,C),(A,D),(C,D),(D,E),(E,B)}

G2=(V2,E2) V2={A,B,C,D,E,F} E2={<A,C>,<B,A>,<B,D>,<E,C>, <C,F>,<F,B>)}

加权图或网

图的基本操作包括: (1)CreateGraph(G):创建图G。 (2)DestoryGraph(G):销毁图G。 (3)LocateVertex(G,v):确定v在图G中的位置。 (4)GetVertex(G,i):取出第i个顶点的值。 (5)FirstAdjVertex(G,v):求v的第一个邻接点。

(6)NextAdjVertex(G,v,w):已知w是v的某个邻接点,求v的下一个邻接点。 (7)InsertVertex(G,u):增加顶点u。 (8)DeleteVetrex(G,v):删除v及与v相关联的弧。 (9)InsertArc(G,v,w):增加一条从v到w的弧。 (10)DeleteArc(G,v,w):删除v到w的弧。

6.1.2 图的基本术语 无向图和有向图 边集E(G)为无向边的集合,为无向图。 边集E(G)为有向边的集合,为有向图。 v2 v5 v1 6.1.2 图的基本术语 无向图和有向图 边集E(G)为无向边的集合,为无向图。 边集E(G)为有向边的集合,为有向图。 v2 v5 v1 v3 v4 v1 v2 v4 v3

6.1.2 图的基本术语 端点和邻接点 无向图中,若存在一条边(vi,vj),则称vi、vj为该边的两个端点,并称它们互为邻接点。 v2 6.1.2 图的基本术语 端点和邻接点 无向图中,若存在一条边(vi,vj),则称vi、vj为该边的两个端点,并称它们互为邻接点。 v2 v5 v1 v3 v4

起点和终点 有向图中,若存在边<vi,vj>,则称该边是顶点vi的出边,顶点vj的入边;并称vi为起始端点(或起点),vj为终止端点(或终点); v1 v2 v3 v4

度、入度和出度 无向图顶点 v 的度是和 v 相关联的边的数目,记做D(v)。 v2 v5 v1 v3 v4 v3 的度为 3

有向图顶点 v 的度分为出度和入度。 顶点 v 的度为 D(v) = ID(v) + OD(v)。 v1 v2 v4 v3 以 v 为头的弧的数目称为 v 的入度,记ID(v) ; 以v 为尾的弧的数目称为 v 的出度,记OD(v); 顶点 v 的度为 D(v) = ID(v) + OD(v)。 v1 v2 v4 v3

子图 假设图 G=(V, E) 和 G’=(V’, E’) ,如果 V’  V,且 E’  E,则称 G’ 为 G 的子图。 v1 v3 求子图 v1 v1 v2 v4 v3 v1 v4 v3 v1 v2 v4 v3

无向完全图和有向完全图 无向图若具有n(n-1)/2条边,称为无向完全图。

无向完全图和有向完全图 有向图具有n(n-1)条边,称为有向完全图。

稀疏图和稠密图 边较少(边数e<<nlog2n)的图称为稀疏图。边较多的图称为稠密图。 路径和路径长度 无向图 G 中若存在一条有穷非空序列 w = v0 e1 v1 e2 v2 … ek vk ,其中 vi 和 ei 分别为顶点和边,则称 w 是从顶点 v0 到 vk 的一条路径。 v0 v1 v2 vk-1 vk . . . e1 e2 ek 路径的长度是路径上的边的数目。

简单路径 若路径 w 的顶点 v0 , v1 , … , vk 互不相同,则称 w 为简单路径。 …

回路(环) 若一条路径上的开始点和结束点为同一个顶点,则称该路径为回路(环)。 开始点与结束点相同的简单路径称为简单回路(简单环)。 一个图如果不存在环,称为无环图。

连通、连通图和连通分量 无向图中 v 到 v’ 有路径,称连通的。 无向图 中任意两个顶点 vi , vj 都是连通的,称连通图。 连通分量:无向图中的极大连通子图。

A B C L H D E F G 非连通图 连通分量为 A B C L H F G H D E

强连通图和强连通分量 有向图 中每一对 vi, vj ∈V,vi≠vj ,从 vi 到 vj 和 从 vj 到 vi 都存在路径,G 是强连通图。 有向图中的极大强连通子图称强连通分量。 v1 v2 v4 v3 强连通分量 v1 v4 v3 v2

生成树、生成森林 无向图是连通的,包含所有顶点并含有最少边的连通子图称为无向图的生成树。

A B C L H J 生成树 A B C L H J A B C L H J A B C L H J

权和网 图的边或弧赋予相关的数值称为权。 带权的图称为网。 带权的无向图称为无向网。 带权的有向图称为有向网。 无向图G不是连通的,G的每个连通分量的生成树组成的森林为生成森林。 权和网 图的边或弧赋予相关的数值称为权。 A B C D E 5 3 8 2 1 4 9 7 带权的图称为网。 带权的无向图称为无向网。 带权的有向图称为有向网。

6.2 图的存储结构 6.2.1邻接矩阵 G 具有 n 个顶点 和 m 条边或弧 ,G 的邻接矩阵是 n×n 阶矩阵,记为 A(G) 。 6.2 图的存储结构 6.2.1邻接矩阵 G 具有 n 个顶点 和 m 条边或弧 ,G 的邻接矩阵是 n×n 阶矩阵,记为 A(G) 。 存放 n 个顶点信息和 n2 条边或弧信息。

aij 定义: aij= 1 vi 与 vj 不相邻 vi 与 vj 相邻

vi 的度是邻接矩阵第 i 行或第 i 列的元素之和。 【例6.1】无向图 G A(G) = 1 2 3 4 5 1 2 3 4 5 0 1 0 1 0 v1 v2 1 0 1 0 1 0 1 0 1 1 1 0 1 0 0 v3 0 1 1 0 0 v4 v5 vi 的度是邻接矩阵第 i 行或第 i 列的元素之和。

【例6.2】有向图 G vi 出度是第 i 行的元素之和。 vi 入度是第 i 列的元素之和。 v1 v2 v3 v4 A(G) = 1 2 3 4 1 2 3 4 v1 v2 v4 v3 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 vi 出度是第 i 行的元素之和。 vi 入度是第 i 列的元素之和。

无向图 有向图 无向图的邻接矩阵是对称矩阵。 有向图的邻接矩阵一般不对称。 ★无向图可以压缩存储 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 1 2 3 4 5 1 2 3 4 5 1 2 3 4 1 2 3 4 0 1 1 0 0 0 0 0 0 0 0 1 1 0 0 0 无向图 有向图 无向图的邻接矩阵是对称矩阵。 有向图的邻接矩阵一般不对称。 ★无向图可以压缩存储

aij = wij ∞ vi 与 vj 相邻接 vi 与 vj 不相邻接

带权图(网) 的邻接矩阵 A = 1 2 3 4 5 6 1 2 3 4 5 6 ∞ 5 ∞ 7 ∞ ∞ ∞ ∞ 4 ∞ ∞ ∞ 5 8 ∞ ∞ ∞ ∞ 9 v1 v2 ∞ ∞ 5 ∞ ∞ 6 ∞ ∞ ∞ 5 ∞ ∞ 8 4 3 ∞ ∞ ∞ 1 ∞ 3 7 v3 9 v6 6 5 1 v5 v4 5

带权无向图邻接矩阵的算法 : (1)构造类型定义 #define MAXVEX 100 /*图的顶点个数*/ typedef struct { char vexs[MAXVEX]; /*顶点信息表*/ int edges[MAXVEX][ MAXVEX]; /*邻接矩阵*/ int n,e; /*顶点数和边数*/ }graph;

(2)建邻接矩阵(对无向图) CreateGraph(graph *ga) { int i,j,k,w; printf("请输入图的顶点数和边数:\n"); scanf("%d%d",&(ga->n),&(ga->e)); 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++) /*读入边顶点编号和权值,建立邻接矩阵*/ { scanf("%d,%d,%d”,&i,&j,&w); ga->edges[i][j]=ga->edges[j][i]=w;}} 算法的执行时间是O(n+n2+e),时间复杂度为O(n2)。

6.2.2 邻接表 邻接表存储结构是对图中的每个顶点建立一个带头结点的单链表。

typedef enum{UDG,/*无向图*/ WUDG,/*赋权无向图*/ DG,/*有向图*/ WDG/*赋权有向图*/} GraphKind; typedef struct rnode/*邻接点结点的结构*/ { int adjvexpos; /*顶点在list中的位置*/ struct rnode *next; }RNode; typedef struct { VertexDT data; /*顶点数据域*/ RNode *firstarc; /*链表头指针域*/ }VNode;

逆邻接表

有向图的邻接表和逆邻接表结合在一起,交叉链表。

6.3 图的遍历 从某一顶点出发访遍图中所有顶点,且每一个顶点仅被访问一次,称为图的遍历。 6.3 图的遍历 从某一顶点出发访遍图中所有顶点,且每一个顶点仅被访问一次,称为图的遍历。 避免同一个顶点被访问多次,增设一个数组 visited[0 . . n-1] 指示顶点是否已被访问过。

6.3.1广度优先搜索 (2) 接着依次访问与顶点i有边相连的所有顶点W1,W2,…,Wt; (1) 访问顶点i,将访问标志置为已被访问,即visited[i]=1; (2) 接着依次访问与顶点i有边相连的所有顶点W1,W2,…,Wt; (3) 再按顺序( “先被访问顶点的邻接点”先于“后被访问顶点的邻接点”被访问)访问与 W1,W2,…,Wt有边相连又未曾访问过的顶点;依此类推,直到所有顶点都被访问完为止。

A BDF EG JI CH

无向图的广度优先搜索生成森林

BFS(ALGraph *g,int vi) /*对邻接表g从顶点vi开始广度优先遍历*/ { int i,v,visited[MAXVEX]; int Qu[MAXVEX],front=0,rear=0; /*循环队列*/ RNode *p; for(i=0;i<g->n;i++) /*给visited置初值0*/ visited[i]=0; visited[vi]=1; /*访问初始顶点*/ printf(“%d ”,vi); rear=(rear+1)%MAXVEX; Qu[rear]=vi; /*初始顶点入队列*/ while(front!=rear) /*队列不为空时循环*/

{front=(front+1)%MAXVEX; v=Qu[front]; /*出队列*/ p=g-> list[v]. firstarc ; /*查找v第一个邻接点*/ while(p!=NULL) /*查找v的所有邻接点*/ { if(visited[p->adjvexpos]==0) { visited[p->adjvexpos]=1; printf("%d",p->adjvexpos); /*访问该点并使之入队列*/ rear=(rear+1)%MAXVEX; Qu[rear]=p->adjvexpos; } p=p->next; /*查找v的下一个邻接点*/ } } }

6.3.2深度优先搜索 (1)先访问顶点i,将其访问标记置为访问过,即visited[i]=1; (2)搜索与i有边相连的下一个顶点j,若j未被访问过,则访问它,将visited[j]=1,然后从j开始重复此过程,若j已访问,再看与i有边相连的其它顶点; (3)若与i有边相连的顶点都被访问过,则退回到前一个访问顶点并重复刚才过程,直到所有顶点都被访问完止。

无向图的深度优先搜索

无向图的深度优先搜索生成森林

DFS (ALGraph *g,int vi) /*从vi出发深度优先搜索图*/ { InitStack(S); /*初始化空栈*/ Push(S,vi); while(!Empty(S)) {v=Pop(S); if(!visited(v)) { visit(v);visited[v]=1;} w=FirstAdj(g,v); /*求v的第一个邻接点*/ while(w!=-1) { if(!visited(w)) {Push(S,w); visit(w); visited[w]=1; } w=NextAdj(g,v,w); /*求v相对于w的下一个邻接点*/}}}

6.4 图的连通性 赋权无向连通图的所有生成树中,各边上权值总和最小的生成树称为最小生成树。

6.4.1 普里姆算法 任取一个顶点K作为开始点,令U={k},W=V-U,其中V为顶点集,然后找一个顶点在U中,另一个顶点在W中的边中最短的一条,找到后,将该边作为最小生成树的树边保存起来,并将该边顶点全部加入U集合中,并从W中删去这些顶点,然后重新调整U中顶点到W中顶点的距离, 使之保持最小,重复此过程,直到W为空集止。

若lowcost[i]=0,i在U中; 若0<lowcost[i]<∞,则i在(V-U)中,i和U中的顶点closest[i]构成的边(i,closest[i])是所有与i相邻、另一端在U的具有最小权值的边,最小的权值为lowcost[i]; 若lowcost[i]=∞,表示i与closest[i]之间没有边。 算法每一步扫描数组lowcost,在V-U中找出离U最近的顶点,令其为k,并打印边(k,closest[k])。修改数组lowcost和closest,标记k已经加入U。

对应的普里姆算法: #define INF 32767 /* INF 表示∞ */ Prim(int cost[][MAXVEX],int n,int v) /*输出最小生成树的每条边*/ { int lowcost[MAXVEX],min; int closest[MAXVEX],i,j,k; for(i=0;i<n;i++) /*给lowcost[]和closest[]置初值*/ { lowcost[i]=cost[v][i]; closest[i]=v; } for(i=1;i<n;i++) /*找出n-1个顶点*/ { min=INF ;

if(lowcost[j]!=0&&lowcost[j]<min) { min=lowcost[j]; k=j ; } for(j=0;j<n; j++) /*在(V-U)中找出离U最近的顶点k*/ if(lowcost[j]!=0&&lowcost[j]<min) { min=lowcost[j]; k=j ; } printf(" 边%d 权%d",closest[k],k); lowcost[k]=0; /*标记k已经加入U*/ for(j=0;j<n;j++) /*修改数组lowcost和closest*/ if(cost[k][j]!=0&&cost[k][j]<lowcost[j]) {lowcost[j]=cost[k][j]; closest[j]=k; }}} 普里姆算法的时间复杂度为O(n2)

6.4.2克鲁斯卡尔算法 从赋权无向图G中取所有顶点作为一个森林F,从图G中滤出一条边,要求该边上的权值尽可能小并且该边两端的顶点必须分别处在森林F中的两棵树上,用该边把森林F中的两棵树合并成一棵,森林F中便减少了一棵树,按这个规则继续从图中滤出边来合并森林中的两棵树,直到森林中只有一棵树为止。

对应的克鲁斯卡尔算法: typedef struct { int u; /* 边的起始顶点*/ int v; /*边的终止顶点*/ int w; /*边的权值*/ }Edge; void Kruskal(Edge E[ ],int n,int e) { int i,j,m1,m2,sn1,sn2,k; int vset[MAXV]; for(i=0;i<n;i++) vset[i]=i; /*初始化辅助数组*/ k=1; /*k表示当前构造最小生成树的第几条边,初值为1*/

j=0 ; /*E中边的下标,初值为0*/ while(k<n) /*生成的边数小于n时循环*/ { m1=E[j].u;m2=E[j].v; /*取一条边的头尾顶点*/ sn1=vset[m1];sn2=vset[m2];/*分别得到两个顶点所属的集合编号*/ if(sn1!=sn2) /*两顶点属不同的集合,该边是最小生成树的边*/ { printf("(%d,%d):%d",m1,m2,E[j].w); k++; /*生成边数增1*/ for(i=0;i<n;i++) /*两个集合统一编号*/ if(vset[i]==sn2) /*集合编号为sn2的改为sn1*/ vset[i]=sn1; } j++; /*扫描下一条边*/

6.5 最短路径 1、从甲地到乙地是否有公路? 2、从甲地到乙地可能有多少条公路,哪条公路最短或花费最小? 6.5 最短路径 对于一个汽车司机或乘客来说: 1、从甲地到乙地是否有公路? 2、从甲地到乙地可能有多少条公路,哪条公路最短或花费最小? 最短路径是指经过的边的权值之和最小,而不是边的数目最小。

分两种情况: 单源最短路径; 每对顶点之间最短路径。 在赋权有向图中,顶点vi到顶点vj所有经过的弧上权值之和最小的路径就是vi到vj的最短路径。 分两种情况: 单源最短路径; 每对顶点之间最短路径。

6.5.1 单源最短路径 s[0,…,n-1]用于标记已找到最短路径的顶点。设顶点v为源点,集合s的初态只包含顶点v。 s[i]=

path[i]保存从源点v到终点vi当前最短路径中的前一个顶点编号,它的初值为源点v的编号 (v到vi有边)或-1(v到vi无边)。 dist[i]=cost[v][i]:从源点到其他各顶点距离,从V-S中选出一个顶点vu,使dist[u]的值最小。把u加入S中:dist[j]和dist[u]+cost[u][j]选择较小的值作为新的dist[j]。重复…。 path[i]保存从源点v到终点vi当前最短路径中的前一个顶点编号,它的初值为源点v的编号 (v到vi有边)或-1(v到vi无边)。

V1 50 <v0,v1> 45 <v0,v2,v3,v1> V2 10 <v0,v2> V3 ∞ 25 <v0,v2,v3> V4 <v0,v4> V5 Vj u (v0,v2) (v0,v2,v3) (v0,v2,v3,v1) (v0,v2,v3,v1,v4)

else path[i]=-1; } s[v]=1;path [v]=0; /*源点编号v放入 s*/ for(i=0;i<n;i++) /*循环直到所有顶点的最短路径都求出*/ { mindis=INF; u=-1; for(j=0;j<n; j++) /*选取不在s中且具有最小距离的顶点u*/ if(s[j]==0&&dist[j]<mindis) { u=j ; mindis=dist[j]; void Dijkstra(int cost[][MAXVEX],int n,int v) { int dist[MAXVEX],path[MAXVEX]; int s[MAXVEX]; int mindis,i,j,u,pre; for(i=0;i<n;i++) {dist[i]=cost[v][i]; /*距离初始化*/ s[i]=0; /*s[]置空*/ if(cost [v][i]<INF) /*路径初始化*/ path[i]=v;

if(u!=-1) /*找到最小距离的顶点u*/ { s[u ]=1; /*将u加入s中*/ for(j=0;j<n;j++) {if(i!=v) { printf(“ %d->%d:”,v,i); if(s[i]==1) {printf(“路径长度 %d”,dist[i]); pre=i; printf(“ 路径逆序为”); while(pre!=v) /*一直回溯到初始顶点*/ { printf(“%d,”,pre); pre=path[pre];} printf(“%d\n”,pre); } else printf(“不存在路径\n”);}}} if(u!=-1) /*找到最小距离的顶点u*/ { s[u ]=1; /*将u加入s中*/ for(j=0;j<n;j++) /*修改不在s中的顶点距离*/ if(s[j]==0) if(cost[u][j]<INF&&dist[u]+cost[u][j]<dist[j]) {dist[j]=dist[u]+cost[u][j] ; path[j]=u; } } } for(i=0;i<n;i++) /*输出最短路径的长度,路径逆序输出*/

6.5.2 每对顶点之间的最短路径 从vi到vj有边,则从vi到vj存在一条长度为cost[i][j]的路径。该路径不一定是最短路径,尚需进行n次试探。 首先考虑路径(vi,v0,vj)是否存在(即判断弧(vi,v0)和(v0,vj)是否存在)。如果存在,则比较其路径长度。 再增加一个顶点v1,即如果(vi ,…,v1)和(v1, …,vj) ,再增加一个顶点v2,继续进行试探。依此类推,直至经过n次比较,最后求得的必是从vi到vj的最短路径。

现定义一个n阶方阵序列:A -1,A0, …,Ak,…,An-1,其中: A-1[i][j]=cost[i][j] (0≤i≤n-1,0≤j≤n-1) Ak+1[i][j]=min{Ak[i][j],Ak[i][k+1]+Ak[k+1][j]} (-1≤k≤n-2 )

【算法6.7】 #define MAXVEX 100 #define INF 32767 void Floyed(int cost[][MAXVEX],int n) {int A[MAXVEX][MAXVEX],path[MAXVEX][MAXVEX];

{ printf(" %d ->%d",i,j); if(A[i][j]==INF) { if(i!=j) int i,j,k,pre; for(i=0;i<n;i++) /*置初值*/ for(j=0;j<n; j++) { A[i][j]=cost[i][j]; path[i][j]=-1;} for(k=0;k<n;k++) { for(i=0;i<n; i++) for(j=0;j<n;j++) if(A[i][j]>(A[i][k]+A[k][j])) { A[i][j]=A[i][k]+A[k][j]; path[i][j]=k; } } for(i=0;i<n;i++) /*输出最短路径*/ for(j=0;j<n;j++) if(i!=j) { printf(" %d ->%d",i,j); if(A[i][j]==INF) { if(i!=j) printf("不存在路径\n"); } else { printf("长度%d",A[i][j]); printf("路径为%d",i); pre=path[i][j]; while(pre!=-1) { printf("%d",pre); pre=path[pre][j]; } printf("%d\n",j); } }}

6.6 AOV网与拓扑排序 有向图代表一个完整的任务,图中的每个顶点代表一个活动而弧代表活动的先后顺序,称为AOV网。

拓扑排序是把AOV网中的顶点转换成一个线性序列,有些顶点是有优先关系的,有些顶点不存在优先关系,人为地加入一些优先关系。

(1)在有向图中选择一个入度为0的顶点vi输出。 (2)把vi删除,并删除所有以vi为弧尾顶点的弧。 重复,直到没有顶点或没有入度为0的顶点为止。

算法实现 采用邻接表作为有向图的存储结构 ; 表头结点中增加一个存放顶点入度的count ; 入度为 0 的顶点即为没有前驱的顶点 ; 当某个顶点的入度为 0时,输出此顶点,同时将该顶点的所有后继顶点的入度减1; 为了避免重复检测入度为0的顶点,设立一个栈St,以存放入度为0的顶点。

拓扑排序算法: printf("%d",i); /*输出顶点*/ p=adj[i].firstarc; /*找第一个相邻顶点*/ TopSort(VNode adj[],int n) {int i,j; int St[MAXV],top=-1; /*栈St的指针为top*/ RNode *p; for(i=0;i<n; i++) if(adj[i].count==0) /*入度为0的顶点入栈*/ { top++; St[top]=i;} while(top>-1) /*栈不为空时循环*/ { i=St[top];top--; /*出栈*/ printf("%d",i); /*输出顶点*/ p=adj[i].firstarc; /*找第一个相邻顶点*/ while(p!=NULL) { j=p->adjvexpos; adj[j].count--; if(adj[j].count==0) /*入度为0的相邻顶点入栈*/ {top++; St[top]=j;} p=p->nextarc; /*找下一个相邻顶点*/} }}

6.7 AOE网与关键路径 赋权有向图代表一个完整任务,每个顶点代表一个事件,而弧代表活动,弧上权值代表活动持续时间,称为AOE网。

最关心问题: 整个工程需要时间是多少? 哪些活动的提前或延期将直接影响整个工程的进度?

关键路径是指从工程开始点到工程结束点所经过的各条路径中时间耗费总量最大的路径。

ee(k)是指从工程开始点e1到顶点ek所耗费的最长时间。 (1)事件的最早发生时间 ee(k)是指从工程开始点e1到顶点ek所耗费的最长时间。 假设ee(1)=0,则ek的最早发生时间为: ee(k)=max{ee(j) + < ej, ek>上的权值}

(2)事件的最迟发生时间 不推迟整个工期的前提下,事件ek允许的最晚发生时间。ek的最迟发生时间:le(k)=min{le(j) - <ek, ej>上的权值}

(3)工程活动ai的最早开工时间e(i) 等于事件ek的最早发生时间。e(i)=ee(k)。

(4)工程活动ai的最晚开工时间 不推迟整个工期的前提下,ai必须开工的最晚时间。 l(i)=le(j) - <ek, ej>上的权值 d(i)=l(i)-e(i)是活动的时间余量。 l(i)=e(i)表示活动ai是没有时间余量的关键活动。

算法步骤为: (1)入度为0的工程开始点e1出发,令ee[1]=0,按正向拓扑排序顺序求其余各事件的最早发生时间ee[i](2≤i≤n )。如果得到的正向拓扑序列中事件顶点个数小于AOE网中顶点数n,则说明网中有环,操作失败,结束;否则继续下一步。 (2)从出度为0的工程结束点en出发,令le[n]=ee[n],按逆向拓扑排序顺序求其余各事件的最迟发生时间le[i](2≤i≤n-1 ); (3)根据各事件顶点的ee和le值,求每个工程活动的最早开工时间e和最迟开工时间l。若某个工程活动的最早开工时间e和最迟开工时间l相等,则为关键活动。

【例6.1】AOE网的关键路径。 解:求所有事件的最早发生时间如下: ee(1)=0; ee(2)=3 ee(3)=4; ee(4)=ee(2)+2=5 ee(5)=Max{ee(2)+1, ee(3)+3)}=7; ee(6)=ee(3)+5=9 ee(7)=Max{ee(4)+6, ee(5)+8)}=15; ee(8)=ee(5)+4=11 ee(9)=Max{ee(8)+10,ee(6)+2)}=21; ee(10)=Max{ee(8)+4,ee(9)+1}=22 ee(11)=Max{ee(7)+7,ee(10)+6}=28

所有事件的最迟发生时间如下: le(11)=ee(11)=28; le(10)=le(11)-6=22 le(9)=le(10)-1=21; le(8)=Min{le(10)-4,le(9)-10}=11 le(7)=le(11)-7=21; le(6)=le(9)-2=19 le(5)=Min{le(7)-8,le(8)-4)}=7; le(4)=le(7)-6=15 le(3)=Min{le(5)-3,le(6)-5)}=4;le(2)=Min{le(4)-2,le(5)-1)}=6 le(1)=Min{le(2)-3,le(3)-4)}=0

求所有活动的e()、 活动a1:e(1)=ee(1)=0 活动a2:e(2)=ee(1)=0 活动a3:e(3)=ee(2)=3 活动a4:e(4)=ee(2)=3 活动a5:e(5)=ee(3)=4 活动a6:e(6)=ee(3)=4 活动a7:e(7)=ee(4)=5 活动a8:e(8)=ee(5)=7 活动a9:e(9)=ee(5)=7 活动al0:e(10)=ee(6)=9 活动a11:e(11)=ee(7)=15 活动a12:e(12)=ee(8)=11 活动a13:e(13)=ee(8)=11 活动a14:e(14)=ee(9)=21 活动a15:e(15)=ee(10)=22

活动a1:l(1)=le(2)-3=3, d(1)=3 活动a2:l(2)=le(3)-4=0, d(2)=0 活动a3:l(3)=le(4)-2=13, d(3)=10 活动a4:l(4)=le(5)-1=6, d(4)=3 活动a5:l(5)=le(5)-3=4, d(5)=0 活动a6:l(6)=le(6)-5=14, d(6)=10 活动a7:l(7)=le(7)-6=15, d (7)=10 活动a8:l(8)=le(7)-8=13, d(8)=6 活动a9:l(9)=le(8)-4=7, d(9)=0 活动al0:l(10)=le(9)-2=19,d(10)=10 活动a11:l(11)=le(11)-7=21, d(11)=6 活动a12:l(12)=le(10)-4=18, d(12)=-7 活动a13:l(13)=le(9)-10=11, d(13)=0 活动a14:l(14)=le(10)-1=21, d(14)=0 活动a15:l(15)=le(11)-6=22, d(15)=0

结 束!