下面我简称Dijkstra算法为D算法。D算法是在图中求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径,直到求出点源到其他各个节点的最短路径。那D算法的基本思想是什么呢?将节点设为一个集合V,再将集合V划为两部分S和V-S。S集合中的节点的最短路径已经确定,V-S中的节点的最短路径待定。
假设我们有下面这个图(画的有点拉):
从源点出发只经过S中的节点到达V-S的路径称为特殊路径。D算法的贪心策略就是选择最短的特殊路径长度dist[t],将节点t加入集合S中,同时更新数组dist。
我们正式梳理一下算法的过程即出现的变量。
1,设定G[][]为邻接矩阵存图,数组dist[i]记录从源节点到节点i的最短路径长度,如果没有则赋值无穷INF。p[i]记录最短路径上的节点i的之直接前驱。用一个bool型数组flag[i]判断节点i在哪一个集合,如果在S则flag[i]=true,否则为false(二进制表示)。
2,初始化。假设u为源节点,令集合S=u,对V-S集合中的节点i初始化为dist[i]=G[u][i](横向来看,就直接等于边权w),如果源节点u到节点i有边相连,初始化p[i]=u,否则p[i]=-1。
3,找最小路径在V-S集合中。dist[]最小的节点t就是V-S集合中距离源点u最近的节点,并将节点加入集合S。
4,松弛操作*。对V-S里的所有节点j,看是否可以借助t得到更短的路径。如果有,即表达式:dist[j]>dist[t]+G[t][j],那么进行操作更新dist[j]=dist[t]+G[t][j],并记录j的直接前驱为t,即p[j]=t。画个图演示一下啊:::
一开始是u->j的路径,但后来发现u->t->j更短,那就进行所谓的松弛操作。
知道怎么做了,我们演示一遍。
先在2,3节点里面比较dist数组(度)的大小,发现2节点的度最小,放进去。
之后在与2相连的4,3节点里面找。先看4,4的直接前驱t是2,所以更改dist[4]数组INF为8(从节点1加到节点4,但必须经过节点2)。之后看3,重复上述操作改dist[3]为4,那么联系上面的松弛操作,1->2->3的度小于(必须是小于才行)1->3的度,改改改!p[3]=2。现在与2相邻的节点的度计算完了,那就找最小的了,那必然是1->2->3,把节点3纳入集合S,之后重复上述操作。
下面是更改之后的数据:
这里我在给出全部走完一遍的数据:::
现在是要啥有啥就差代码了。
#include<iostream>
#define N 10000
#define INF 0x3f3f3f3f
using namespace std;
int G[N][N], dist[N], n, m;
int p[N];
bool flag[N];
void dij(int u)
{
int i, j;
for (i = 1; i <= n; i++)//初始化啊
{
dist[i] = G[u][i];
flag[i] = false;
if (dist[i] == INF)
p[i] = -1;
else
p[i] = u;
}
dist[u] = 0;
flag[u] = true;
for (i = 1; i < n; i++)//找最小度的操作,n-1次
{
int temp = INF, t = u;//临时变量
for (j = 1; j <= n; j++)
{
if (!flag[j] && dist[j] < temp)
{
temp = dist[j];
t = j;
}
}
if (t == u)
return;
flag[t] = true;
for (j = 1; j <= n; j++)//判断是否可以松弛
{
if (!flag[j] && dist[j] > dist[t] + G[t][j])
{
dist[j] = dist[t] + G[t][j];//更新最短距离
p[j] = t;//记录前驱
}
}
}
}