弗洛伊德算法介绍
弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。
Floyd优缺点分析
优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。
缺点:时间复杂度比较高,不适合计算大量数据。
Floyd算法与Dijkstra算法的不同
1.Floyd算法是求任意两点之间的距离,是多源最短路径,而Dijkstra(迪杰斯特拉)算法是求一个顶点到其余所有顶点的最短路径,是单源最短路径。
2.Floyd算法可以算带负权的,而Dijkstra(迪杰斯特拉)算法是不可以算带负权的。并且Floyd算法不能算负权回路。
3.Dijkstra(迪杰斯特拉)算法时间复杂度一般是o(n^2),Floyd算法时间复杂度是o(n^3),Dijkstra(迪杰斯特拉)算法比Floyd算法快。
4.Floyd算法属于动态规划,Dijkstra(迪杰斯特拉)算法属于贪心算法。
/**
* 实验题目:
* 采用弗洛伊德算法求带权有向图的最短路径
* 实验目的:
* 领会弗洛伊德算法求带权有向图中多源最短路径的过程和相关算法设计
* 实验内容:
* 编写一个程序,实现求带权有向图中多源最短路径的弗洛伊德算法。并
* 输出如图8.1所示的带权有向图G中所有两个顶点之间的最短路径长度和最短
* 路径。
*/
图8.1
#include <stdio.h>
#include <malloc.h>
#define INF 32767 //定义∞
#define MAXV 100 //最大顶点个数
typedef char InfoType;
/*-------------------------以下定义邻接矩阵类型---------------------------*/
typedef struct
{
int no; //顶点编号
InfoType info; //顶点信息
}VertexType; //顶点类型
typedef struct
{
int edges[MAXV][MAXV]; //邻接矩阵数组(用一个二维数组存放顶点间关系(边或弧)的数据)
int n; //顶点数
int e; //边数
VertexType vexs[MAXV]; //存放顶点信息(用一个一维数组存放图中所有顶点数据)
}MatGraph; //完整的图邻接矩阵类型
//邻接表表示法-将每个顶点的邻接点串成一个单链表
/*-----------以下定义邻接表类型--------------*/
typedef struct ArcNode
{
int adjvex; //该边的邻接点编号
struct ArcNode *nextarc; //指向下一条边的指针
int weight; //该边的相关信息,如权值(用整型表示)
}ArcNode; //边结点类型
typedef struct VNode
{
InfoType info; //顶点其他信息
int cnt; //存放顶点入度,仅用于拓扑排序
ArcNode *firstarc; //指向第一条边
}VNode; //邻接表结点类型
typedef struct
{
VNode adjlist[MAXV]; //邻接表头结点数组
int n; //图中顶点数
int e; //图中边数
}AdjGraph; //完整的图邻接表类型
/*-------------------------邻接矩阵的基本运算算法---------------------------*/
/*------------由边数组A、顶点数n和边数e创建图的邻接矩阵g--------------------*/
void CreateMat(MatGraph &g, int A[MAXV][MAXV], int n, int e)
{
int i, j;
g.n = n;
g.e = e;
for(i = 0; i < g.n; i++)
for(j = 0; j < g.n; j++)
g.edges[i][j] = A[i][j];
}
/*------------输出邻接矩阵g--------------------*/
void DispMat(MatGraph g)
{
int i, j;
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
if(g.edges[i][j] != INF)
printf("%4d", g.edges[i][j]);
else
printf("%4s", "∞");
}
printf("\n");
}
}
/*-------------------------邻接表的基本运算算法---------------------------*/
/*-------------------由边数组A、顶点数n和边数e创建图的邻接表G--------------------*/
void CreateAdj(AdjGraph *&G, int A[MAXV][MAXV], int n, int e)
{
int i, j;
ArcNode *p;
G = (AdjGraph *)malloc(sizeof(AdjGraph));
for(i = 0; i < n; i++) //给邻接表中所有头结点的指针域置初值NULL
{
G->adjlist[i].firstarc = NULL;
}
for(i = 0; i < n; i++) //检查邻接矩阵中的每个元素
{
for(j = n - 1; j >= 0; j--)
{
if(A[i][j] != 0 && A[i][j] != INF) //存在一条边
{
p = (ArcNode *)malloc(sizeof(ArcNode)); //创建一个结点p
p->adjvex = j; //邻接点编号
p->weight = A[i][j]; //边的权重
p->nextarc = G->adjlist[i].firstarc; //采用头插法插入结点p
G->adjlist[i].firstarc = p;
}
}
}
G->n = n;
G->e = e;
}
/*-------------------输出邻接表G--------------------*/
void DispAdj(AdjGraph *G)
{
ArcNode *p;
for(int i = 0; i < G->n; i++)
{
p = G->adjlist[i].firstarc;
printf("顶点%d: ", i);
while(p != NULL)
{
printf("%3d[%d]->", p->adjvex, p->weight); //邻接点编号[权重]
p = p->nextarc;
}
printf("∧\n");
}
}
/*-------------------销毁图的邻接表G--------------------*/
void DestroyAdj(AdjGraph *&G)
{
ArcNode *pre, *p;
for(int i = 0; i < G->n; i++)
{
pre = G->adjlist[i].firstarc; //pre指向第i个单链表的首结点
if(pre != NULL)
{
p = pre->nextarc;
while(p != NULL) //释放第i个单链表的所有边结点
{
free(pre);
pre = p;
p = p->nextarc;
}
free(pre);
}
}
free(G); //释放头结点数组
}
/*---------------输出图g中所有两个顶点之间的最短路径和最短路径长度-------------*/
static void Dispath(MatGraph g, int A[][MAXV], int path[][MAXV])
{
int i, j, k, s;
int apath[MAXV], d; //存放一条最短路径中间顶点(反向)及其顶点个数
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
if((i != j) && (A[i][j] != INF)) //若顶点i和j之间存在路径
{
printf(" 从%d到%d的路径为:", i, j);
k = path[i][j];
d = 0; apath[d] = j; //路径上添加终点
while((k != -1) && (k != i)) //路径上添加中间点
{
d++;
apath[d] = k;
k = path[i][k];
}
d++; apath[d] = i; //路径上添加起点
printf("%d", apath[d]); //输出起点
for(s = d - 1; s >= 0; s--) //输出路径上的中间顶点
printf(",%d", apath[s]);
printf(" \t路径长度为:%d\n", A[i][j]);
}
}
}
}
/*---------------求图g中所有两个顶点之间的最短路径长度和最短路径---------------*/
static void Floyd(MatGraph g)
{
int A[MAXV][MAXV], path[MAXV][MAXV];
int i, j, k;
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
A[i][j] = g.edges[i][j];
if((i != j) && (g.edges[i][j] < INF))
{
path[i][j] = i; //顶点i到j有边时
}
else
{
path[i][j] = -1; //顶点i到j没有边时
}
}
}
for(k = 0; k < g.n; k++) //依次考察所有顶点
{
for(i = 0; i < g.n; i++)
{
for(j = 0; j < g.n; j++)
{
if(A[i][j] > A[i][k] + A[k][j])
{
A[i][j] = A[i][k] + A[k][j]; //修改最短路径长度
path[i][j] = path[k][j]; //修改最短路径
}
}
}
}
//输出最短路径
Dispath(g, A, path);
}
int main(void)
{
MatGraph g;
int A[MAXV][MAXV] = {
{0, 5, INF, 7, INF, INF},
{INF, 0, 4, INF, INF, INF},
{8, INF, 0, INF, INF, 9},
{INF, INF, 5, 0, INF, 6},
{INF, INF, INF, 5, 0, INF},
{3, INF, INF, INF, 1, 0}
};
int n = 6; //图中的顶点数
int e = 10; //图中的边数
CreateMat(g, A, n, e); //建立图8.1的邻接矩阵
printf("有向图G的邻接矩阵:\n");
DispMat(g);
printf("弗洛伊德算法求解结果:\n");
Floyd(g);
return 0;
}
测试结果:
有向图G的邻接矩阵:
0 5 ∞ 7 ∞ ∞
∞ 0 4 ∞ ∞ ∞
8 ∞ 0 ∞ ∞ 9
∞ ∞ 5 0 ∞ 6
∞ ∞ ∞ 5 0 ∞
3 ∞ ∞ ∞ 1 0
弗洛伊德算法求解结果:
从0到1的路径为:0,1 路径长度为:5
从0到2的路径为:0,1,2 路径长度为:9
从0到3的路径为:0,3 路径长度为:7
从0到4的路径为:0,3,5,4 路径长度为:14
从0到5的路径为:0,3,5 路径长度为:13
从1到0的路径为:1,2,0 路径长度为:12
从1到2的路径为:1,2 路径长度为:4
从1到3的路径为:1,2,0,3 路径长度为:19
从1到4的路径为:1,2,5,4 路径长度为:14
从1到5的路径为:1,2,5 路径长度为:13
从2到0的路径为:2,0 路径长度为:8
从2到1的路径为:2,0,1 路径长度为:13
从2到3的路径为:2,0,3 路径长度为:15
从2到4的路径为:2,5,4 路径长度为:10
从2到5的路径为:2,5 路径长度为:9
从3到0的路径为:3,5,0 路径长度为:9
从3到1的路径为:3,5,0,1 路径长度为:14
从3到2的路径为:3,2 路径长度为:5
从3到4的路径为:3,5,4 路径长度为:7
从3到5的路径为:3,5 路径长度为:6
从4到0的路径为:4,3,5,0 路径长度为:14
从4到1的路径为:4,3,5,0,1 路径长度为:19
从4到2的路径为:4,3,2 路径长度为:10
从4到3的路径为:4,3 路径长度为:5
从4到5的路径为:4,3,5 路径长度为:11
从5到0的路径为:5,0 路径长度为:3
从5到1的路径为:5,0,1 路径长度为:8
从5到2的路径为:5,4,3,2 路径长度为:11
从5到3的路径为:5,4,3 路径长度为:6
从5到4的路径为:5,4 路径长度为:1