图
文章目录
一、基本概念
1.图的定义
图(Graph):由两个集合V(G)和E(G)组成的,记为G=(V,{E})
其中:V是顶点的有穷非空集;E是边(弧)的有限集
2.约定符号
-
V
顶点有穷非空集合
-
VR
顶点关系的集合
-
E
边或弧的集合
-
n
图中顶点数目
-
e
边或弧的数目
-
G
图
-
N
网
3.分类
-
有向图
-
术语
-
顶点
图中的数据元素
-
入度
顶点v的入度:以v为头的弧的数目,记为ID(v)
-
出度
顶点v的出度:以v为尾的弧的数目,记为OD(v)
-
度
顶点v的度TD(v)= ID(v)+ OD(v)
-
-
弧,弧头,胡尾
<v,w>表示从v到w的一条弧,w称作弧头,v称作弧尾
-
邻接关系
如果弧<v,w> ∈E,称v邻接到w或w邻接自v
-
有向完全图
n个顶点、有n(n-1)条弧的有向图
-
-
定义
有向图G1 =(V1,{A}) 其中,V1 = {v1,v2,v3,v4} A = {<v1,v2>, <v1,v3>,
<v3,v4>, <v4,v1>} -
有向图的连通性
-
v到w的路径
在有向图G=(V,{E})中由顶点v经有向弧至w的顶点序列
-
v和w是连通的
顶点v到w以及w到v都有路径存在
-
强连通图
有向图 G 的任意两点之间都连通
-
强连通分量
有向图的极大强连通子图
-
-
有向树
如果一个有向图恰有一个顶点入度为0,其余顶点入度均为1,则是有向树。
-
有向图的生成森林
由若干有向树组成,含有图中全部顶点,但只有足以构成若干棵不相交的有向树的弧。
-
-
无向图
-
术语
-
顶点
图中的数据元素
-
顶点的度
顶点v相关联的边的数目即v的度,记为TD(v)
-
-
邻接点
如果边(v,w)∈E,v和w互为邻接点,或v和w相邻接
边(v,w)依附于顶点v和w,或边(v,w)与v和w相关联 -
边
(v,w)表示v,w之间的一条边
-
无向完全图
n个顶点、有n(n-1)/2 条边的无向图
-
-
定义
无向图G2 =(V2,{E}) 其中,V2 = {v1,v2,v3,v4,v5} E = {(v1,v2),
(v1,v3),(v2,v4), (v2,v5), (v3,v5),(v4,v5)} -
无向图的连通性
-
v到w的路径
在无向图G=(V,{E})中由顶点v经无向边至w的顶点序列
-
v和w是连通的
顶点v和w之间有路径存在
-
连通图
无向图的任意两点之间都连通
-
连通分量
无向图的极大连通子图
-
-
无向连通图的生成树
无向连通图的极小连通子图。包含图的全部n个顶点和足以构成一棵树的n-1条边。在生成树中添加一条边之后,必定会形成回路或环
-
4.子图
G=(V,{E}),G’= (V’,{E’}),若V’属于 V,E’属于 E,则G’是G的子图
5.路
-
简单路径
路径序列中顶点不重复出现
-
回路或环
第一个顶点和最后一个顶点相同的路径
-
简单回路或简单环
除第一个顶点和最后一个顶点外,其余顶点不重复出现的回路。
6.其他术语
-
稀疏图
很少条边或弧(e<nlog2n)的图
-
稠密图
有很多条边或弧的图
-
边或弧的权值
与弧或边相关的数。可以表示从一个顶点到另一个顶点的距离、花费的代价、所需的时间等
-
网
边或弧上带权的图
7.ADT
ADT Graph {
数据对象V: V是具有相同特性的数据元素的集合,即顶点集
数据关系R: R={VR} VR={<v,w>|……}
基本操作P:
CreateGraph(&G,V,VR); DestroyGraph (&G);
GetVex (G,v); PutVex (&G,v,value);
LocateVex(G,u);
FirstAdjVex (G,v);
NextAdjVex(G,v,w);
InsertVex (&G,v); DeleteVex (&G,v);
InsertArc (&G,v,w); DeleteArc (&G,v,w);
DFSTraverse (G,Visit());
BFSTraverse (G,Visit());
} ADT Graph
二、存储结构
1.邻接矩阵(数组)
用一个1-D数组存放顶点的元素信息
用一个二维数组存储顶点之间的关系,其中:
A[i][j]=1,i到j有弧且i≠j;
A[i][j]=0,其他情况
网的邻接矩阵:
A[i][j]= wi,j,若<vi,vj>或(vi,vj)∈E
A[i][j]= ∞,反之
-
存储类型的定义
typedef enum { DG, DN, UDG, UDN } GraphKind; typedef struct ArcCell { VRType adj; infoType *info; }ArcCell,AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; typedef struct { VertexType vexs[MAX_VERTEX_NUM]; //顶点向量 AdjMatrix arcs; int vexnum,arcnum; GraphKind kind; }MGraph;
-
建图
Status CreateGraph(MGraph& G) {//在邻接矩阵存储结构上根据图的种类调用具体构造算法 printf("please input the kind of graph\n"); printf("0构造有向图 1构造有向网 2构造无向图 3构造无向网\n"); scanf("%d",&G.kind); switch (G.kind) { case DG:return CreateDG(G);//构造有向图 case DN:return CreateDN(G);//构造有向网 case UDG:return CreateUDG(G);//构造无向图 case UDN:return CreateUDN(G);//构造无向网 default:return ERROR; } }
-
算法
-
构造有向图
Status CreateDG(MGraph &G) //在邻接矩阵存储结构上,构造有向图G { scanf(&G.vexnum,&G.arcnum);//读入顶点数和边数目for(i=0;i<G.vexnum;i++) scanf(&G.vexs[i]);//构造顶点向量 for(i=0;i<G.vexnum;i++) //邻接矩阵初始化 for(j=0;j<G.vexnum;j++) G.arcs[i][j]=0; for(k=0;k<G.arcnum;k++)//构造邻接矩阵 { scanf(&v1,&v2,);//读入一条边依附的顶点 i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1、v2在图中的位置 G.arcs[i][j]=1;//边<v1,v2> } return OK; }//CreateDG
-
构造有向网
Status CreateDN(MGraph &G) //在邻接矩阵存储结构上,构造有向网G { scanf(&G.vexnum,&G.arcnum);//读入顶点数和边数目for(i=0;i<G.vexnum;i++) scanf(&G.vexs[i]);//构造顶点向量 for(i=0;i<G.vexnum;i++) //邻接矩阵初始化 for(j=0;j<G.vexnum;j++) G.arcs[i][j]=INFINITY; for(k=0;k<G.arcnum;k++)//构造邻接矩阵 { scanf(&v1,&v2,&w);//读入一条弧依附的顶点及权值 i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1、v2在图中的位置 G.arcs[i][j]=w;//边<v1,v2>的权值 } return OK; }//CreateDN
-
构造无向图
Status CreateUDG(MGraph &G) //在邻接矩阵存储结构上,构造无向图G { scanf(&G.vexnum,&G.arcnum);//读入顶点数和边数目for(i=0;i<G.vexnum;i++) scanf(&G.vexs[i]);//构造顶点向量 for(i=0;i<G.vexnum;i++) //邻接矩阵初始化 for(j=0;j<G.vexnum;j++) G.arcs[i][j]=0; for(k=0;k<G.arcnum;k++)//构造邻接矩阵 { scanf(&v1,&v2,);//读入一条边依附的顶点 i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1、v2在图中的位置 G.arcs[i][j]=1;//边<v1,v2> G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称弧<v2,v1 > } return OK; }//CreateUDG
-
构造无向网
Status CreateUDN(MGraph &G) //在邻接矩阵存储结构上,构造无向网G { scanf(&G.vexnum,&G.arcnum);//读入顶点数和边数目for(i=0;i<G.vexnum;i++) scanf(&G.vexs[i]);//构造顶点向量 for(i=0;i<G.vexnum;i++) //邻接矩阵初始化 for(j=0;j<G.vexnum;j++) G.arcs[i][j]=INFINITY; for(k=0;k<G.arcnum;k++)//构造邻接矩阵 { scanf(&v1,&v2,&w);//读入一条边依附的顶点及权值 i=LocateVex(G,v1);j=LocateVex(G,v2);//确定v1、v2在图中的位置 G.arcs[i][j]=w;//边<v1,v2>的权值 G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称弧<v2,v1 > } return OK; }//CreateUDN
-
-
算法分析
-
优缺点
优点
易判定任两个顶点之间是否有边或弧存在。
适合存储有向图、无向图、有向网、无向网
缺点:在边稀疏(e<<n(n-1)/2)时,浪费存储空间 -
时间复杂度讨论
有n个顶点和e条边的图
邻接矩阵初始化的时间复杂度为O(n2)
输入顶点编号,建立邻接矩阵的时间复杂度为O(e)
输入顶点值,建立邻接矩阵的时间复杂度为O(ne)
总的时间复杂度为:O(n2+ne)
-
-
-
定位顶点
-
算法分析
遍历顶点向量,定位顶点,失败则返回-1
-
代码实现
int LocateVex(MGraph G, VertexType v) { int i; for (i = 0; i < G.vexnum; i++) if (G.vexs[i] == v) return i; return -1; }//定位顶点坐标 ```
-
2.邻接表
图的一种链式存储结构,适用于有向图和无向图。对图中每个顶点建立一个单链表,单链表中的结点表示依附于该顶点的边(对有向图来说是以该顶点为弧尾的弧)
-
存储类型定义
typedef struct ArcNode//表结点 { int adjvex;//弧指向顶点的位置 struct ArcNode* nextarc;//指向下一弧的指针 }ArcNode; typedef struct Vnode//头结点 { VertexType data;//顶点信息 ArcNode* firstarc;//指向第一条弧的指针 }VNode, AdjList[MAX_VERTEX_NUM]; typedef struct { AdjList vertices;//头节点向量 int vexnum, arcnum; //图的当前顶点数和弧数 int kind; //图的种类 }ALGraph;
-
建图
-
算法
- 构造有向图
- 构造有向网
- 构造无向图
- 构造无向网
-
算法分析
-
优缺点
优点:易找到任一顶点的第一个邻接点和下一个邻接点
适合存储有向图(网)、无向图(网)
缺点:难以直接判定任意两个顶点之间是否有边或弧相连。需搜索第i和第j个单链表。不及邻接矩阵方便。 -
时间复杂度讨论
时间复杂度的讨论:
邻接表头结点初始化的时间复杂度为O(n)
输入顶点编号,建立邻接点链表的时间复杂度为O(e)
输入顶点值,建立邻接点链表的时间复杂度为O(ne)
总的时间复杂度为:O(n+ne)
-
-
三、基本算法
1.遍历
-
定义
从图中某个顶点出发遍访图中其余顶点,且使每个顶点仅被访问一次的过程.
-
意义
对非线性结构线性化。遍历算法是求解图的连通性、拓扑排序、求关键路径等算法的基础。
-
两种遍历方式
-
深度优先搜索DFS (Depth First Search)
-
算法思想
深度优先遍历:类似于树的先序遍历,是其推广
①从图中某个顶点v出发,访问此顶点;
②依次从v的各个未被访问的邻接点出发深度优先遍历图,直至图中所有和v有路径相通的顶点都被访问到
③若图中还有顶点未被访问(非连通图),则另选图中一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。 -
算法代码
Boolean visited[MAX_VERTEX_NUM]; Status (*VisitFunc)(int v); //全局函数指针变量 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); }//DFSTraverse void DFS( Graph G, int v) { //从v出发深度优先遍历图G visited[v] = TRUE; VisitFunc(v); //访问顶点v for(w=FirstAdjVex(G,v); w>=0; w=NextAdjVex(G,v,w)) if ( !visited[w] ) DFS(G,w); } //DFS
-
算法分析
在遍历图时,对每个顶点至多调用一次DFS函数,因为一旦某个顶点被标志成已被访问,就不再从它出发进行搜索。
遍历图的实质上是对每个顶点查找其邻接点的过程。其耗费的时间取决于所采用的存储结构。
用邻接矩阵存储图时,查找所有顶点的邻接点需要O(n2);
用邻接表存储图时,查找所有顶点的邻接点需要O(e);
深度优先遍历图的算法的时间复杂度与采用的存储结构有关
以邻接矩阵做图的存储结构时,深度优先遍历的时间复杂度为O(n2)
以邻接表做图的存储结构时,深度优先遍历的时间复杂度为O(n+e)。
-
-
广度优先搜索BFS (Breadth First Search)
-
算法思想
类似于树的层序遍历,是其推广。
广度优先遍历的实质是以v为起点,由近及远,依次访问和v有路径相通且路径长度为1、2、……的顶点。
步骤:
①从图中的某个顶点v出发,访问此顶点;
②依次访问v的所有未被访问过的邻接点,之后按这些邻接点被访问的先后次序依次访问它们的邻接点,直至图中所有和v有路径相通的顶点都被访问到;
③若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。 -
算法代码
void BFSTraverse(Graph G,Status(*Visit)(int v)) { //对图G进行广度优先遍历 for(v=0;v<G.vexnum;++v) visited[v] = FALSE; InitQueue(Q); for( v=0;v<G.vexnum;++v ) if(!visited[v]) //v没有被访问 { 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); //访问w EnQueue(Q,w); //w入队列 } //if } //while } //if } //BFSTraverse
-
-
-
辅助向量visited[]的使用
-
使用原因
因为图中任意顶点都可能和其余顶点相邻接,所以在访问了某顶点后,可能沿另外的某条路径搜索,而后又回到此顶点上,为了避免同一顶点被多次访问,在遍历图的过程中,必须记下每个已访问过的顶点。
-
使用方法
设置一个辅助数组visited[0…n-1],它的初始值置为“假”,表示顶点未被访问过,一旦访问了顶点i,就置visited[i]的值为“真”或者被访问时的次序号。
-
2.求无向图的连通分量
-
算法思想
利用图的遍历算法来判定一个无向图是否是连通图
对于无向连通图,从任一顶点出发,进行深度/广度优先遍历,就可访遍图中所有顶点;
对于非连通图,它有几个连通分量,就需要从几个顶点出发进行遍历。
修改DFSTraverse和BFSTraverse中的语句,可以判断无向图的连通分量个数。如:
sum=0;
for ( v=0; v <G.vexnum; ++v )
if ( ! visited[v] )
{ sum++;
…
}
3.生成树或生成森林
-
深度优先生成树
由深度优先遍历得到的为深度优先生成树
-
广度优先生成树
由广度优先遍历得到的为广度优先生成树
4.最小生成树
-
定义
生成树中各边权值(代价)之和最小的树。
-
最小生成树的应用
在n个城市之间选取n-1条线路架设连通的通信网使总费用最低问题 在n个顶点的连通网上构造最小生成树
-
MST性质
设N=(V,{E})是连通网,U是V的一个非空子集。若(u,v)是所有满足u∈U, v∈V-U的边中代价最小的边,则必存在一棵包括边(u, v)的最小生成树。 (可用反证法证明)
-
Prim算法(点)
-
Prim算法步骤
设N=(V,{E})是连通网,T=(V,{TE})表示N的最小生成树,TE为最小生成树边的集合,初始为空集。则Prim算法的执行过程:
Step1:令U={u},u∈V(u是网中任意一个顶点),TE={};
Step2:在u∈U,v∈V-U的边(u,v)∈E中寻找一条代价最小的边(u,v)并入TE,同时将顶点v并入U;
Step3:重复Step2,直至U=V,此时TE中必有n-1条边,而T={V,{TE}}是N的一棵最小生成树。 -
时间复杂度
只与顶点数有关, 与网中的边数无关
适用于求边稠密的网的最小生成树
-
-
Kruscal算法(边)
-
Kruskal算法步骤
步骤:
假设连通网N=(V,{E}),T=(V,{TE})表示N的最小生成树,TE为最小生成树上边的集合。初始时令TE为空集。
Step1:令最小生成树T的初态为只有n个顶点的非连通图T=(V,{TE}),TE={}。
Step2:从权值最小的边(u,v)开始,若该边依附的两个顶点落在T的不同连通分量上,则将此边加入到TE中,即TE=TE∪(u,v),否则舍弃此边,选择下一条代价最小的边。
Step3:重复Step2,直至TE所有顶点在同一连通分量上。此时T=(V,{TE})就是N的一棵最小生成树。 -
时间复杂度
O(e*loge),只与边数有关, 与网中的顶点数无关
适合于求边稀疏的网的最小生成树
-
-
5.拓扑排序
-
概念和术语
-
DAG图
-
有向无环图(DAG图)
无环的有向图
-
应用
- 描述有公共因子的表达式
- 描述工程和系统的进行过程
-
有向图中检测是否存在环
-
拓扑排序
对有向图进行拓扑排序,若网中所有顶点都在它的拓扑有序序列中,则不存在环。
-
深度优先遍历
利用深度优先遍历判断有向图是否存在环的方法:
由图中某点出发进行深度优先搜索遍历,遇到回边时,判断该回边指向顶点的深度优先遍历过程是否已结束,若发现回边时弧头顶点的深度优先遍历过程还未退出,则图中有环。
-
-
-
AOV网
-
AOV(Activity On Vertex)网
有向图可用来描述一项工程或系统的完成过程。在这种图中,顶点表示活动,有向弧表示活动之间的优先关系,如
<vi,vj>表示活动vi必须先于活动vj进行。其中vi是vj的直接前驱,vj是vi的直接后继。若从顶点vi到vk有一条路径,则vi是vk的前驱、vk是vi的后继; -
用途:描述工程项目或系统进行的次序
-
-
偏序
若集合 X 上的关系R是传递的、自反的、反对称的,则称R是集合X上的偏序关系。可指集合中部分成员之间可比较。
-
全序
若关系R 是集合 X 上的偏序关系,如果对于属于X的每个x,y,必有xRy 或yRx
,则称R是集合X上的全序关系。可指集合中全部成员之间可比较。 -
拓扑排序
由集合上的偏序得到该集合上的全序的操作。这个全序被称为拓扑有序。
-
-
拓扑排序步骤
-
Step1:
在有向图中选一个无前驱的顶点输出之;
-
Step2:
从有向图中删去此顶点及所有以它为尾的弧;
-
Step3:
重复前2步,直到图中顶点全部输出,此时图中无环;或图不空但找不到无前驱的顶点,此时图中有环。
-
-
算法
-
算法思想
基于邻接表存储结构的图的拓扑排序算法:
辅助数组Indegree[]:记录每个顶点的入度
辅助结构:暂存入度为零的顶点以避免重复检测 -
算法代码
Status TopologicalSort(ALGraph G) { FindIndegree(G,indegree); InitStack(S); //用到第3章中栈的基本操作 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; //输出顶点数加1 for(p=G.vertices[i].firstarc; p; p=p->nextarc) { k=p->adjvex; if(!(--indegree[k])) Push(S,k); } }//while if(count<G.vexnum) return ERROR; else return OK; //无回路 }// TopologicalSort
-
6.关键路径
-
术语
-
AOE-网
-
AOE-网(Activity On Edge
AOE-网(Activity On Edge):一个有向无环网,顶点表示事件,弧表示活动,弧上权值表示活动持续的时间。
-
通常用来估算工程完成时间
-
源点
入度为0
-
汇点
出度为0
-
路径长度
AOE网中路径上各活动持续时间之和。
-
关键路径
从源点到汇点路径长度最长的路径。
-
-
活动ai的最早开始时间e(i)
活动ai的最早开始时间e(i):是从源点v0到vj的最长路径长度。
-
活动ai的最迟开始时间l(i)
是不推迟工程完成的前提下,该活动允许的最迟开始时间。
-
活动ai时间余量
l(i)-e(i)
-
关键活动
l(i)=e(i)的活动。 关键路径上的活动都是关键活动
-
事件vk的最早发生时间Ve(k)
事件vk的最早发生时间Ve(k)=从源点v0到vk的最长路径长度 Ve(0)=0;
Ve(k)=Max{Ve(j)+dut(<j,k>), <j,k>∈T,所有j} -
事件vj的最迟开始时间Vl(j)
保证汇点vn-1在Ve(n-1)时刻完成的前提下,事件vj最迟允许开始的时间。 Vl(n-1) =
Ve(n-1)=从源点到汇点的最长路径长度; Vl(j)=Min{Vl(k)-dut(<j,k>), <j,k>∈T,所有k}
-
-
求关键活动
-
从Ve(0)=0向汇点方向递推
从Ve(0)=0向汇点方向递推,顶点vk的最早开始时间ve(k)=Max{Ve(j)+dut(<j,k>)},在拓扑有序的前提下进行
-
从vl(n-1)=ve(n-1)向源点方向推
从vl(n-1)=ve(n-1)向源点方向推,顶点vj的最迟开始时间vl(j)=Min{vl(k)-dut(<i,j>)},在逆拓扑有序前提下进行
-
7.最短路径
-
最短路径问题
从图中某一顶点到达另一顶点的路径可能不止一条,求其中一条路径使得沿此路径上各弧上的权值总和最小。称路径的第一个顶点为源点,最后一个顶点为终点。
-
算法
-
Dijkstra算法
迪杰斯特拉提出了一个按路径长度递增次序产生最短路径的算法——迪杰斯特拉算法
一般情况下,假设S为已求得最短路径终点的集合,可以证明:下一条最短路径(设其终点为vk)可能是弧<v0,vk>或者是中间只经过S中的顶点到达顶点vk的路径(反证法)-
步骤
- Step1:置final[i] 为FALSE,从源点v0出发到图上其余各顶点vi (vi∈V)可能的最短路径长度的初值为:
D[i]= G.arcs[LocateVex(G,v0)][i]; - Step2:选择vj ,使得D[j]={D[i]|vi∈V-S}, 则vj就是当前求得的一条从v0出发的最短路径的终点,令final[j]为TRUE,即S=S∪{vj};
- Step3:修改从v0出发到集合V-S上任一顶点vk可达的最短路径长度:如果D[j] +arcs[j][k]<D[k],则更新D[k]为D[k]=D[j]+arcs[j][k];
- Step4:重复Step2、Step3共n-1次,由此求得从v0到其余各顶点的n-1条最短路径,并且它们是按路径长度递增的。
- Step1:置final[i] 为FALSE,从源点v0出发到图上其余各顶点vi (vi∈V)可能的最短路径长度的初值为:
-
代码
Status ShortestPath_DIJ(MGraph G, int v0, int P[MAX_VERTEX_NUM][ MAX_VERTEX_NUM], VRType D[MAX_VERTEX_NUM]) {//求邻接矩阵表示的图的最短路径的Dijkstra算法 初始化D[],P[][],final[]数组; D[v0]=0; final[v0]=TRUE; //v0并入S for(i=1;i<G.vexnum;i++) //对于其余G.vexnum-1个顶点 { select(D,final,min,v); final[v]=TRUE; //找到一条从v0到v的最短路径 for(w=0; w<G.vexnum;w++) //更新当前最短路径和距离 { if(!final[w]&& (min+G.arcs[v][w]<D[w])) { D[w]=min+G.arcs[v][w]; P[w]=P[v]; P[w][w]=TRUE; //w是从v0到w的最短路径上的顶点 }//if }//for }//for }//ShortestPath_DIJ
-
-
Floyd算法
-
(1)在vi和vj之间加入顶点v0,若<vi,v0>和<v0,vj>存在,则(vi,v0,vj)存在,比较<vi,vj>和(vi,v0,vj)的路径长度,取较短者为从vi到vj的中间顶点序号不大于0的最短路径。
-
(2)在vi和vj之间加入顶点v1,若(vi,…,v1)和(v1,…,vj)分别是当前找到的中间顶点序号不大于0的最短路径,则路径(vi,…,v1,…vj)存在,和上一步求出的vi和vj的中间顶点序号不大于0的最短路径比较后,取较短者为从vi到vj的中间顶点序号不大于1的最短路径。
-
(3)以此类推,在vi和vj之间加入顶点vk,若(vi,…,vk)和(vk,…,vj)分别是当前找到的中间顶点序号不大于k-1的最短路径,则将路径(vi,…,vk,…vj)和已经求得的vi和vj的中间顶点序号不大于k-1的最短路径比较后,取较短者为从vi到vj的中间顶点序号不大于k的最短路径。
-
经过n次比较后,最后求得的必是vi到vj的最短路径。按此方法可以同时求得每一对顶点之间的最短路径。
-
-