Floyed | Dijkstra(优先队列优化) | SPFA(优先队列优化) | |
时间复杂度 | o(n^3) | o(n+m)(logm) | o(km) |
基本思想 | 动态规划 | 贪心 | 贪心 |
适用范围 | 无负环图 | 无负权图 | 无负环图 |
多源最短路 | 不含负权图的低复杂度解 | 含负权边时的单源最短路求解 |
算法简洁描述:n,N 点的数量
m, M 边的数量
G[ x ][ y ] 图,表示从 x 点到 y 点的边的权值
dis[ i ][ j ] 路径,表示当前情况下 i 点到 j 点的最短路径长度
通过不断选取中间点来更新从点 i 到点 j 的最短路径
更新方式:将现有的点 i 到 j 的最短距离 与 已知点 i 到中间点 k 的最小距离 + k 到 j 的路径距离 进行比较
公式表述:循环次序:dis[ i ][ j ] = min( dis[ i ][ j ] , dis[ i ][ k ] + G[ k ][ j ] )
for temp in spot:
for start in spot:
for end in spot:
基本实现:
目标:
代码:读入一个含有n条边的无负环无向图,求出图中任意两点之间的最短路径
#include
using namespace std;
//n->点数 m->边数
#define N 1000
#define M 2000
int n, m;
//邻接矩阵图数组
int G[N+1][N+1];
//最短路数组
int dis[N+1][N+1];
//算法内容
void Floyed()
{
for(int k = 1; k <= n; k++){ //中间点
for(int i = 1; i <= n; i++){ //起始点
if(i == k) continue;
for(int j = 1; j <= n; j++){ //末尾点
if(i == j) continue;
dis[i][j] = min(dis[i][j], dis[i][k] + G[k][j]);
}
}
}
}
int main(void)
{
memset(dis, 0x3f, sizeof(dis));
scanf("%d%d", &n, &m);
//读入无负环无向图
for(int i = 1; i <= m; i++){
int x, y, v;
scanf("%d%d%d", &x, &y, &v);
G[x][y] = G[y][x] = v;
}
Floyed();
return 0;
}
2.Dijkstra算法 变量声明:
算法简洁描述:n, N 图的点
m, M 图的边start 起始点
INF 无穷大
链式前向星edge x 点下标, v 边权值, next 连接点
vis[ i ] 标记从起始点到点 i 的最短路径是否已经更新
dis[ i ] 表示从起始点到点 i 的最短路径值
结构体 ty 收纳点信息,包括点下标 x 和最短路径值 dis
priority_queueq; 边权升序排列的优先队列
以既定起点的最短路径值为 0 开始,不断地从当前已确定最短路径的点中选取dis值最小的点,用选取点去更新所有相邻点的最短路径
过程描述:1.更新起始点 dis 为 0,将起始点的信息(下标 和 dis值) 放进优先队列
2.从优先队列中取出一个未用于更新且 dis 值最小的点信息,然后将该点信息丢出队列
3.用取出的点去更新邻近点的 dis 值,并将新更新的点的信息放进优先队列
4.重复2、3 *** 作,直到优先队列元素为空
其实如果利用优先队列优化,而且每个队列元素拿出来用完就丢掉,就不需要判断取出的点是否曾用于更新,这里为了突出这个先决条件,特意声明
简单实例模拟(变量意义看上述):现在求从点 1 到各点的最短路径值:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |||||||||
dis[1] | vis[1] | dis[2] | vis[2] | dis[3] | vis[3] | dis[4] | vis[4] | dis[5] | vis[5] | dis[6] | vis[6] | dis[7] | vis[7] | dis[8] | vis[8] | |
初始化更新 | 起点的dis值为0,其余dis值为INF,起点最短路径已确定,vis为true | |||||||||||||||
0 | T | INF | F | INF | F | INF | F | INF | F | INF | F | INF | F | INF | F | |
第一次更新 | 与起点邻接的点有3、4、2,更新dis,vis | |||||||||||||||
0 | T | 0+6 | T | 0+5 | T | 0+2 | T | INF | F | INF | F | INF | F | INF | F | |
第二次更新 | 在已经更新的点中选取dis最小且未用于更新最短路的点4作为新的起点,更新所有能更新的最短路径,这次只有点5可更新 | |||||||||||||||
0 | T | 6 | T | 4 | T | 2 | T | 2+4 | T | INF | F | INF | F | INF | F | |
第三次更新 | 选取已经更新的点中dis值最小且未用于更新最短路的点3作为起点,更新最短路 | |||||||||||||||
0 | T | 6 | T | 4 | T | 2 | T | 6 | T | 4+5 | T | 4+6 | T | INF | F | |
第四次更新 | 继续按上述方式选点,出现dis相同的两点,发现选择点2已经不能更新,所以选择点5 | |||||||||||||||
0 | T | 6 | T | 4 | T | 2 | T | 6 | T | 9 | T | 10 | T | 6+7 | T |
与计算机跑出的结果相同:
基本实现: 目标:代码:读入一个含有 n 个点的无负权无向图,求出图中从起始点 start 到任意点的最短路径
#include
using namespace std;
//边&点
#define N 100000
#define M 200000
int n, m, start;
//链式前向星存图
int cnt = 0, head[M+1];
struct Node{
int x, v, next;
}edge[2*N+1];
void addedge(int x, int y, int v)
{
edge[++cnt].v = v;
edge[cnt].x = y;
edge[cnt].next = head[x];
head[x] = cnt;
}
//
int dis[N+1];
bool vis[N+1];
struct ty{
int dis, x;
bool operator < (const ty &a) const{
return dis > a.dis;
}
}temp;
priority_queueq;
//Dijkstra算法
void Dijkstra(int st)
{
//将初始点信息放进队列
dis[st] = 0;
temp.dis = 0; temp.x = st;
q.push(temp);
//
while(!q.empty())
{
//找到当前队列中边权最小点
temp = q.top(); q.pop();
int x = temp.x;
if(vis[x]) continue;
vis[x] = true;
//利用取出的点更新最短路
for(int i = head[x]; i != -1; i = edge[i].next){
int node = edge[i].x;
//将可更新点更新,并放进队列
if(dis[node] > dis[x] + edge[i].v){
dis[node] = dis[x] + edge[i].v;
ty ne;
ne.dis = dis[node]; ne.x = node;
q.push(ne);
}
}
}
}
int main(void)
{
memset(head, -1, sizeof(head));
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
//存图
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++){
int x, y, v;
scanf("%d%d%d", &x, &y, &v);
addedge(x, y, v);
addedge(y, x, v);
}
scanf("%d", &start);
Dijkstra(start);
return 0;
}
3.SPFA算法 变量声明
简洁描述:n, N 图的点
m, M 图的边start 起始点
链式前向星edge x 点下标, v 边权值, next 连接点
vis[ i ] 标记从起始点到点 i 的最短路径是否已经更新
dis[ i ] 表示从起始点到点 i 的最短路径值
queueq; 存放点下标的队列
SPFA思路和过程都和Dijkstra相似,不同的是SPFA不会去研究被用于更新最短路的点dis值是否是最小的
过程描述:1.更新起始点 dis 为 0,放进队列
2.从队列中取出一个点
3.用取出的点更新所有与其相邻的点的 dis 。并且,如果当前队列中没有新更新过的点,就将新更新的点放进队列
4.重复2、3 *** 作,直到队列为空
基本实现: 目标:代码:读入一个含有 n 个点的无负权无向图,求出图中从起始点 start 到任意点的最短路径
#include
using namespace std;
//边&点
#define N 100000
#define M 200000
int n, m, start;
//链式前向星存图
int cnt = 0, head[M+1];
struct Node{
int x, v, next;
}edge[2*N+1];
void addedge(int x, int y, int v)
{
edge[++cnt].v = v;
edge[cnt].x = y;
edge[cnt].next = head[x];
head[x] = cnt;
}
//
int dis[N+1];
bool vis[N+1];
queueq;
//SPFA
void spfa(int st)
{
//起点初始化,放进队列
dis[st] = 0; vis[st] = 1;
q.push(st);
while(!q.empty())
{
//拿出队首元素
int x = q.front(); q.pop();
vis[x] = false;
//更新最短路径值
for(int i = head[x]; i != -1; i = edge[i].next){
int ne = edge[i].x;
if(dis[ne] > dis[x] + edge[i].v){
dis[ne] = dis[x] + edge[i].v;
//如果该元素现在不在队列,放进队列
if(!vis[ne]){
q.push(ne);
vis[ne] = true;
}
}
}
}
}
int main(void)
{
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
memset(head, -1, sizeof(head));
//存图
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++){
int x, y, v;
scanf("%d%d%d", &x, &y, &v);
addedge(x, y, v);
addedge(y, x, v);
}
scanf("%d", &start);
spfa(start);
//输出计算结果
for(int i = 1; i <= n; i++){
printf("dis %d: %2d ", i, dis[i]);
if(i %2 == 0) cout <
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)