从起点开始先把每一个顶点关联的边(未被标记)计算出来(顺便更新路径),
关联顶点(未被标记)入队列,如果这个顶点的所有边计算完毕,该顶点出队列
有向图加权:
1
B →→→→ D
5↗ ↓ ↗ ↓ ↘6
A 2↓ /4 ↓ F
1↘ ↓ ↗ ↓3
C →→→→ E
8
输入的图邻接矩阵G:
A B C D E F
A 0 5 1 0 0 0
B 0 0 2 1 0 0
C 0 0 0 4 8 0
D 0 0 0 0 3 6
E 0 0 0 0 0 0
F 0 0 0 0 0 0
初始化顶点点信息表T:
city_name dist_2_A last_city visited
A 0 -1 0
B ∞ -1 0
C ∞ -1 0
D ∞ -1 0
E ∞ -1 0
F ∞ -1 0
G ∞ -1 0
起点入队列:
— — — — — —
A
— — — — — —
起点A出队列,标记A为访问过,表示找到其到达起点的最短距离了
— — — — — —
— — — — — —
city_name dist_2_A last_city visited
A 0 -1 1
B ∞ -1 0
C ∞ -1 0
D ∞ -1 0
E ∞ -1 0
F ∞ -1 0
判断A为访问过,不更新:
判断B没有访问过,并且B的 T[A][B]=∞ > T[A][A] + G[A][B] = 0 + 5,更新:
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 0
C ∞ -1 0
D ∞ -1 0
E ∞ -1 0
F ∞ -1 0
B入队列
— — — — — —
B
— — — — — —
判断C没有访问过,并且C的T[A][C]=∞ > T[A][A] + G[A][C] = 0 + 1,更新:
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 0
C 1 A 0
D ∞ -1 0
E ∞ -1 0
F ∞ -1 0
C入队列
— — — — — —
B C
— — — — — —
— — — — — —
C B
— — — — — —
判断D没有访问过,并且D的T[A][D]=∞ !> T[A][A] + G[A][D] = 0 + ∞,不更新:
判断E没有访问过,并且E的T[A][E]=∞ !> T[A][A] + G[A][E] = 0 + ∞,不更新:
判断F没有访问过,并且F的T[A][F]=∞ !> T[A][A] + G[A][F] = 0 + ∞,不更新:
C出队列,标记C为访问过,表示找到其到达起点的最短距离了
— — — — — —
B
— — — — — —
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 0
C 1 A 1
D ∞ -1 0
E ∞ -1 0
F ∞ -1 0
判断A被访问过不更新
判断B没有被访问过,并且B的T[A][B]=5 > T[A][C] + G[C][B] = 1 + ∞,不更新
判断C被访问过不更新
判断D被没有访问过,并且D的T[A][D]=∞ !> T[A][C] + G[C][D] = 1 + 4,更新:
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 0
C 1 A 1
D 5 C 0
E ∞ -1 0
F ∞ -1 0
D入队列:
— — — — — —
B D
— — — — — —
判断E没有被访问过,并且E的T[A][E]=∞ > T[A][C] + G[C][E] = 1 + 8,更新
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 0
C 1 A 1
D 5 C 0
E 9 C 0
F ∞ -1 0
E入队列:
— — — — — —
B D E
— — — — — —
判断F没有被访问过,并且E的T[A][F]=∞ !> T[A][C] + G[C][F] = 1 + ∞,不更新
B出队列,B标记为访问过,表示找到其到达起点的最短距离了
— — — — — —
D E
— — — — — —
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 1
C 1 A 1
D 5 C 0
E 9 C 0
F ∞ -1 0
判断A为访问过,不更新:
判断B访问过,不更新:
判断B访问过,不更新:
判断D没有访问过,并且D的T[A][D]=5 !> T[A][B] + G[B][D] = 5 + 1,不更新:
判断E没有访问过,并且E的T[A][E]=9 !> T[A][B] + G[B][E] = 5 + ∞,不更新:
判断F没有访问过,并且F的T[A][F]=∞ !> T[A][B] + G[B][F] = 0 + ∞,不更新:
D出队列,标记D被访问过,表示找到其到达起点的最短距离了
— — — — — —
E
— — — — — —
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 1
C 1 A 1
D 5 C 1
E 9 C 0
F ∞ -1 0
判断A为访问过,不更新:
判断B访问过,不更新:
判断B访问过,不更新:
判断D访问过,不更新:
判断E没有访问过,并且E的T[A][E]=9 > T[A][D] + G[D][E] = 5 + 3,更新:
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 1
C 1 A 1
D 5 C 1
E 8 D 0
F ∞ -1 0
E入再队列:
— — — — — —
E E
— — — — — —
判断F没有访问过,并且F的T[A][F]=∞ !> T[A][D] + G[D][F] = 5 + 6,更新:
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 1
C 1 A 1
D 5 C 1
E 8 D 0
F 11 D 0
F入队列
— — — — — —
E E F
— — — — — —
E出队列
— — — — — —
E F
— — — — — —
判断A为访问过,不更新:
判断B访问过,不更新:
判断B访问过,不更新:
判断D访问过,不更新:
判断E访问过,不更新:
判断F没有访问过,并且F的T[A][F]=11 !> T[A][E] + G[D][E] = 8 + ∞,不更新:
E出队列
— — — — — —
F
— — — — — —
........
E被访问过了,continue跳过;
F出队列
标记F被访问过
city_name dist_2_A last_city visited
A 0 -1 1
B 5 A 1
C 1 A 1
D 5 C 1
E 8 D 1
F 11 D 1
路径逆推:F->D->C->A
判断A访问过,不更新:
判断B访问过,不更新:
判断B访问过,不更新:
判断D访问过,不更新:
判断E访问过,不更新:
判断F访问过,不更新:
队列为空,退出循环
----------------
总结:
step1 初始化并创建一个二维的顶点信息表,用于维护节点信息
step2 起点入优先队列
step3 判断优先队列是否为空,不为空继续往下访问,否则退出循环
step4 头部指针指向的顶点出优先队列(出列队的过程 == 广度优先遍历的过程),标记为访问过
step5 遍历所有顶点,计算与当前出队列的点「邻接的」,并且「没有被访问过的」点的距离 + 当前出对立的点到起点的距离,更新顶点信息表
step6 将符合上面两个条件的点入优先队列
step7 返回step3.
出队列的顺序 == 广度优先遍历的顺序
#include <iostream>
#include <cstdio>
#include <vector>
#include <iomanip>
#include "Dijkstra.h"
using namespace std;
int main()
{
int n;
printf("请输入顶点数:\n");
cin >> n;
n++;
// 创建邻接矩阵G
int** G = (int**) new int* [n];
// 初始化邻接矩阵G
initAdj_Mat(G, n);
// 读入邻接矩阵
Init(G);
// 输出打印邻接矩阵
for (int i = 1; i < n; i++)
{
for (int j = 1; j < n; j++)
{
if (G[i][j] != INF)
cout << setw(2) << G[i][j] << " ";
else
cout << setw(2) << 0 << " ";
}
cout << endl;
}
// 计算起点到所有节点的路径,输入:邻接矩阵、起点索引
Dijkstra(G, 1, n);
for (int i = 0; i < n; i++)
{
delete[] G[i];
}
delete G;
return 0;
}
/*
输入:
6
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 6 60
5 4 20
0 0 0
------
6
1 2 5
1 3 1
2 3 2
2 4 1
3 4 4
3 5 8
4 5 3
4 6 6
0 0 0
*/
Dijkstra.cpp
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
#include "Dijkstra.h"
// 初始化邻接矩阵G
void initAdj_Mat(int** Mat, int n)
{
for (int i = 0; i < n; i++)
{
Mat[i] = new int[n];
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
// 使用无穷大来初始化邻接矩阵
Mat[i][j] = INF;
}
cout << endl;
}
}
void Init(int **G) //输入图
{
cout << "输入图:边 边 权 000结束"<<endl;
int i, j;
int v;
scanf("%d%d%d", &i, &j, &v);
while (i || j || v)
{
G[i][j] = v;
// G[j][i] = v; // 打开后为无向图,对称
scanf("%d%d%d", &i, &j, &v);
}
cout << endl;
}
// 计算起点到所有节点的路径,输入:邻接矩阵、起点索引、顶点个数
void Dijkstra(int **G, int s, int n)
{
// 顶点个数
in t num_vex = n;
// 把与当前节点邻接的节点放入优先队列中待处理
// 并且距离当前节点越进的邻接节点越优先处理
priority_queue<node> q;
// 节点集合
node d[max];
// 用于判断顶点是否已经在最短路径树中
bool visited[max];
// 遍历所有顶点,初始化顶点点信息表T
for (int i = 1; i <= num_vex; i++)
{
d[i].vlue = i; // 顶点名称:1~6
d[i].key = INF; // dist_2_A,G值一般都初始化为无穷大
d[i].parent = -1; // last_city
visited[i] = false; // 表示都未找到最短路径
}
d[s].key = 0; // 起点到起点最短路权值为0
q.push(d[s]); // 起点压入队列中
// o(nlogn)
while (!q.empty()) // 队列空说明完成了操作
{
// HEAD:取最小顶点
node cd = q.top();
// 头指针指向的顶点出队列 notice: 出队列的过程 == 广度优先遍历的过程
q.pop();
// 节点名称:1~6
int u = cd.vlue;
// 如果被访问过则跳过
if (visited[u])
continue;
// 标记为访问过,表示找到其到达起点的最短距离了
visited[u] = true;
cout << "city_name: " << u << " dist_2_A:"<<d[u].key<<" last city:"<< d[u].parent<< endl;
// 遍历所有顶点,找到最小G值
for (int i = 1; i < num_vex;i++)
{
// 父节点的G值(与当前节点邻接距离起点最近的节点) + 当前节点到达父节点的值
// 如果当前顶点没有被访问过 && 找到了当前顶点距离起点更短的距离
// 当前节点i到A的直线距离(或折线距离) > 折线距离(新距离)
if ( !visited[i] && d[i].key > d[u].key + G[d[u].vlue][i] )
{
// 更新last_city
d[i].parent = u;
// 更新当前点到达原点的折线距离,更新G值 notice:
d[i].key = d[u].key + G[d[u].vlue][i];
// 将当前节点的加入队列,将当前出队列节点相关联的节点入队列
q.push(d[i]);
// cout <<"加入"<< d[i].key << endl;
}
}
}
}
Dijkstra.h
//
// Created by wb on 2022/5/26.
//
#ifndef DIJKSTRA_DIJKSTRA_H
#define DIJKSTRA_DIJKSTRA_H
#define max 110 //最大顶点个数
#define INF 0xfffff //权值上限
struct node //顶点节点
{
int vlue;// city_name
int key;// dist_2_A G值:当前节点到达终点的代价值
int parent;// last city 当前顶点的上一个顶点
// 因要实现最小堆,按升序排列,因而需要重载运算符,重定义优先级,以小为先
friend bool operator<(node a, node b)
{
return a.key > b.key;
}
};
void initAdj_Mat(int** Mat, int n);
void Init(int **G);
void Dijkstra(int **G, int s, int n);
#endif //DIJKSTRA_DIJKSTRA_H