图论 (八) 最短路径算法【Floyd算法】

本文深入讲解了Floyd算法,一种用于寻找加权图中多源点之间最短路径的算法。通过动态规划思想,该算法能高效计算出任意两点间的最短路径。文章详细介绍了算法的核心思路、状态转移方程及其实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FloydFloydFloyd算法:又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与DijkstraDijkstraDijkstra算法类似

核心思路

路径矩阵

通过一个图的权值矩阵求出它的每两点间的最短路径矩阵。
从图的带权邻接矩阵A=[map(i,j)]n×nA=[map(i,j)] n×nA=[map(i,j)]n×n开始,递归地进行n次更新,即由矩阵D(0)=AD(0)=AD(0)=A,按一个公式,构造出矩阵D(1)D(1)D(1);又用同样地公式由D(1)D(1)D(1)构造出D(2)D(2)D(2);……;最后又用同样的公式由D(n−1)D(n-1)D(n1)构造出矩阵D(n)D(n)D(n)。矩阵D(n)D(n)D(n)iiijjj列元素便是iii号顶点到jjj号顶点的最短路径长度,称D(n)D(n)D(n)为图的距离矩阵,同时还可引入一个后继节点矩阵pathpathpath来记录两点间的最短路径。
采用松弛技术(松弛操作),对在i和j之间的所有其他点进行一次松弛。所以时间复杂度为O(n3);O(n^3);O(n3);

状态转移方程

其状态转移方程如下: map[i,j]map[i,j]map[i,j]=minminmin{map[i,k]+map[k,j],map[i,j]map[i,k]+map[k,j],map[i,j]map[i,k]+map[k,j],map[i,j]};
map[i,j]map[i,j]map[i,j]表示iiijjj的最短距离,KKK是穷举i,ji,ji,j的断点,map[n,n]map[n,n]map[n,n]初值应该为0,或者按照题目意思来做。
当然,如果这条路没有通的话,还必须特殊处理,比如没有map[i,k]map[i,k]map[i,k]这条路。

在这里插入图片描述

关于FloydFloydFloyd算法理解起来还是比较简单的,按照上面的路径矩阵来说,D(0)D(0)D(0)表示初始矩阵,D(1)D(1)D(1)则可以这样理解,即加入某个中间点,例如上图中加入一个bbb点得到D(1)D(1)D(1),这个D(1)D(1)D(1)代表的是加入bbb点之后路径更新之后的矩阵,就比如说原来aaa没有直接路径到达ccc,而加入bbb点之后aaa就可以通过bbb点到达ccc点,加入的点就相当于中间点

算法实现

在这里直接用权值矩阵代替路径矩阵,就不另设一个; 同时代码实现求出上图所有顶点之间的最短路径

(1) 实现准备:

#define MAXVEX 100
#define INFINITY 65535
int map[MAXVEX][MAXVEX]; //权值矩阵,即邻接矩阵
int p[MAXVEX][MAXVEX];  //后继节点路径
char vex[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };

(2) 初始化节点矩阵:

	int i,j,k;
	for (i = 0; i < n; ++i) {
		for (j = 0; j < n; ++j) {
			p[i][j] = j;
		}
	}

(3) 算法核心:

	for (k = 0; k < n; ++k) {
		for (i = 0; i < n; ++i) {
			for (j = 0; j < n; ++j) {
				//转移方程:如果经过k顶点路径比原两点间路径更短则更新
				if (map[i][j] > map[i][k] + map[k][j]) {
					map[i][j] = map[i][k] + map[k][j];
					p[i][j] = p[i][k];
				}
			}
		}
	}

(4) 打印路径:

	for (i = 0; i < n; ++i) {
		for (j = i + 1; j < n; ++j) {
			cout << vex[i] << "->" << vex[j] << " minimum sum weight: " << map[i][j] << endl;
			int t = p[i][j];
			cout << "path: " << vex[i];
			while (t != j) {
				cout << "->" << vex[t];
				t= p[t][j];
			}
			cout << "->" << vex[t] << endl;
		}
		cout << endl;
	}

完整实现:

#include <iostream>
#include <fstream>
using namespace std;

#define MAXVEX 100
#define INFINITY 65535
int map[MAXVEX][MAXVEX];
int p[MAXVEX][MAXVEX];
char vex[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' };

void Floyd(int map[][MAXVEX], int n) {
	int i,j,k;
	for (i = 0; i < n; ++i) {
		for (j = 0; j < n; ++j) {
			p[i][j] = j;
		}
	}
	for (k = 0; k < n; ++k) {
		for (i = 0; i < n; ++i) {
			for (j = 0; j < n; ++j) {
				if (map[i][j] > map[i][k] + map[k][j]) {
					map[i][j] = map[i][k] + map[k][j];
					p[i][j] = p[i][k];
				}
			}
		}
	}
}

int main() {
	int n,m;
	int i,j,k,cost;
	ifstream in("input.txt");
	in >> n >> m;
	for (i = 0; i < n; ++i) {
		for (j = 0; j < n; ++j) {
			map[i][j] = INFINITY;
		}
	}

	for (k = 0; k < m; ++k) {
		in >> i >> j >> cost;
		map[i][j] = cost;
		map[j][i] = cost;
	}

	Floyd(map, n);

	for (i = 0; i < n; ++i) {
		for (j = i + 1; j < n; ++j) {
			cout << vex[i] << "->" << vex[j] << " minimum sum weight: " << map[i][j] << endl;
			int t = p[i][j];
			cout << "path: " << vex[i];
			while (t != j) {
				cout << "->" << vex[t];
				t= p[t][j];
			}
			cout << "->" << vex[t] << endl;
		}
		cout << endl;
	}

	return 0;
}

input.txtinput.txtinput.txt

9 15
0 1 10
0 5 11
1 2 18
1 6 12
1 7 12
2 3 22
2 7 8
3 4 20
3 6 24
3 7 21
3 8 16
4 5 26
4 8 7
5 6 17
6 8 19

outputoutputoutput

a->b minimum sum weight: 10
path: a->b
a->c minimum sum weight: 28
path: a->b->c
a->d minimum sum weight: 43
path: a->b->h->d
a->e minimum sum weight: 37
path: a->f->e
a->f minimum sum weight: 11
path: a->f
a->g minimum sum weight: 22
path: a->b->g
a->h minimum sum weight: 22
path: a->b->h
a->i minimum sum weight: 41
path: a->b->g->i

b->c minimum sum weight: 18
path: b->c
b->d minimum sum weight: 33
path: b->h->d
b->e minimum sum weight: 38
path: b->g->i->e
b->f minimum sum weight: 21
path: b->a->f
b->g minimum sum weight: 12
path: b->g
b->h minimum sum weight: 12
path: b->h
b->i minimum sum weight: 31
path: b->g->i

c->d minimum sum weight: 22
path: c->d
c->e minimum sum weight: 42
path: c->d->e
c->f minimum sum weight: 39
path: c->b->a->f
c->g minimum sum weight: 30
path: c->b->g
c->h minimum sum weight: 8
path: c->h
c->i minimum sum weight: 38
path: c->d->i

d->e minimum sum weight: 20
path: d->e
d->f minimum sum weight: 41
path: d->g->f
d->g minimum sum weight: 24
path: d->g
d->h minimum sum weight: 21
path: d->h
d->i minimum sum weight: 16
path: d->i

e->f minimum sum weight: 26
path: e->f
e->g minimum sum weight: 26
path: e->i->g
e->h minimum sum weight: 41
path: e->d->h
e->i minimum sum weight: 7
path: e->i

f->g minimum sum weight: 17
path: f->g
f->h minimum sum weight: 33
path: f->a->b->h
f->i minimum sum weight: 33
path: f->e->i

g->h minimum sum weight: 24
path: g->b->h
g->i minimum sum weight: 19
path: g->i

h->i minimum sum weight: 37
path: h->d->i


代码虽然比较简单,但非常巧妙!!!从代码来看,时间复杂度为O(n2)O(n^2)O(n2)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值