Dijkstra算法常用来求图中单个源点到其他各点的最短路径,该算法采用贪心策略,每次都挑选最短路径的顶点。对每个结点使用时可求图中任意两点间的最短路径,不过求图中任意两点间的路径时常使用Floyd算法实现。
本次使用的图为有向带权图,存储结构为邻接矩阵,该存储结构更适合稠密图。
一、求解过程
1.将网(带权的图)中的顶点分为两组,一组为已知最短路径的顶点,一组为未知的。后面将通过此算法不断将未知集中的顶点加入已知集。
2.此算法依赖三个容器,在此使用的是数组。分别为dist[i](存储顶点Vi到起始点的距离),path[i](存储顶点Vi的前驱顶点)和s[i](用来标记是否已经访问过顶点Vi
3.算法将依次将路径最短的顶点加入已知集,每加入一个新顶点时,就要进行一次遍历,将最新找到顶点的邻接点的dist[]更新(松弛),这是由于每当一个顶点进入时,总是这个顶点的邻接点的dist[]会更新。
for (int i = 1; i < n; i++)//n个顶点的图,需要找n-1个顶点的最小路径
{
min = MaxInt;//最开始赋予不与V0相邻的顶点的dist[]
for (int w = 1; w <= n; w++)
if (!S[w] && dist[w] < min) { v = w; min = dist[w]; }//循环,找一条路径最小的边,并将这条边的终点记为v
S[v] = true; //表示v已被收入已知集
for (int w = 1; w <= n; w++) //收入v后开始更新v的其余顶点的dist[]
if (!S[w] && (dist[v] + G.arcs[v][w] < dist[w]))
{
dist[w] = dist[v] + G.arcs[v][w];
Path[w] = v;
}
}
4.最终所有顶点的最小路径都已找到后,path[]其实是倒叙存放路径经过的顶点,使用一个栈结构可将其正序输出。
for (int i = 1; i <= n; i++)
{
cout << G.vexs[v0]<<"到" << G.vexs[i] <<"的最短路径长度为"<< dist[i];
int j = i;
int t = i;
if (Path[j] == -1) cout << endl; //处理v0和与v0不通的顶点,该类顶点和v0之间不存在最短路径,所以path为初始化时的值,此处为-1
if (Path[j] != -1)
{
cout << " " << "路径为";
while (Path[j] != -1)//循环,将一个顶点的前面的所有顶点全部压入栈
{
Push(s, Path[j]);
j = Path[j];
}
while (s.top != s.base)//输出
{
int pathname = -1;
Pop(s, pathname);
cout << G.vexs[pathname] << " ";
}
cout << G.vexs[t];
cout << endl;
}
}
二、全部代码
#include <iostream>
using namespace std;
int const MaxInt = 10000;
#define MVNum 100//最大顶点个数
typedef string VerTexType; //假设顶点的数据类型为字符型
typedef int ArcType;//假设边的权值为整型
typedef struct
{
VerTexType vexs[MVNum + 1];//顶点表
ArcType arcs[MVNum + 1][MVNum + 1]; //邻接矩阵,也即二维数组
int vexnum, arcnum;//图当前的点数和边数
}AMGraph;
typedef struct
{
int stacksize;
int* base;
int *top;
}SqStack;
//图的邻接矩阵表示法
void CreateUDN(AMGraph* G)//创建无向图,采用邻接矩阵表示法
{
cout << "请输入图的顶点数和边数" << endl;
cin >> G->vexnum >> G->arcnum;//输入总顶点数和总边数
for (int i = 1; i <= G->vexnum; i++)
{
cout << "请输入第" << i << "个顶点的信息" << endl;
cin >> G->vexs[i];
}//依次输入点的信息
for (int i = 1; i <= G->vexnum; i++) //初始化邻接矩阵,边的权值均设置为最大值
{
for (int j = 1; j <= G->vexnum; j++)
G->arcs[i][j] = MaxInt;
}
cout << "请输入依次输入各边的两个顶点和权值" << endl;
for (int k = 1; k <= G->arcnum; k++)
{
int v1, v2;//v1和v2为两个点的位置
ArcType w;//w为边的权值
cout << "第" << k << "条边:";
cin >> v1 >> v2 >> w;//输入一条边的依附顶点及权值
G->arcs[v1][v2] = w;
}
}
void DijkstraPath(AMGraph G, int v0)
{
int n = G.vexnum;
bool* S = new bool[n + 1];//判断是否走过
int* dist = new int[n + 1];
int* Path = new int[n + 1];
for (int v = 1; v <= n; v++)//对各个顶点进行初始化
{
S[v] = false;
dist[v] = G.arcs[v0][v];
if (dist[v] < MaxInt) Path[v] = v0;
else Path[v] = -1;
}
S[v0] = true;
dist[v0] = 0; //至此已经完成对S[v],dist[v],path[v]的初始化。
int w = 0;
int v = 0;
int min = MaxInt;
for (int i = 1; i < n; i++)
{
min = MaxInt;
for (w = 1; w <= n; w++)
if (!S[w] && dist[w] < min) { v = w; min = dist[w]; }//循环,找一条路径最小的边
S[v] = true;
for (w = 1; w <= n; w++)
if (!S[w] && (dist[v] + G.arcs[v][w] < dist[w]))
{
dist[w] = dist[v] + G.arcs[v][w];
Path[w] = v;
}
}
void Push(SqStack & s, int e);
void Pop(SqStack & s, int& e);
void InitStack(SqStack & s);
SqStack s;
InitStack(s);
for (int i = 1; i <= n; i++)
{
cout << G.vexs[v0]<<"到" << G.vexs[i] <<"的最短路径长度为"<< dist[i];
int j = i;
int t = i;
if (Path[j] == -1) cout << endl;
if (Path[j] != -1)
{
cout << " " << "路径为";
while (Path[j] != -1)
{
Push(s, Path[j]);
j = Path[j];
}
while (s.top != s.base)
{
int pathname = -1;
Pop(s, pathname);
cout << G.vexs[pathname] << " ";
}
cout << G.vexs[t];
cout << endl;
}
}
}
int main()
{
void CreateUDN(AMGraph * G);
void DijkstraPath(AMGraph G, int v0);
AMGraph G;
CreateUDN(&G);
int v0 = 1;
DijkstraPath(G, v0);
return 0;
}
void InitStack(SqStack& s)
{
s.base = new int[101];
s.top = s.base;
s.stacksize = 101;
}
void Push(SqStack& s, int e)
{
*s.top++ = e;
}
void Pop(SqStack& s, int& e)
{
e = *--s.top;
}
三、时间复杂度
扫描所有未收录顶点-O(|V|)
对每个顶点需要扫描其余顶点更新dist[]-O(|V|²)
参考数据结构第二版-严蔚敏及浙大慕课数据结构-陈越姥姥