Dijkstra算法代码详解(C语言)
一、算法介绍及其总体思想
Dijkstra算法通常用来求一个点到其他点的最短路径。 可以算是贪心算法的一种,其基本思想为:从问题的某个初始解出发,通过一系列的局部最优选择,从而得到全局的最优解。
二、算法流程
以下图为例,分别求0点到1,2,3,4点的最短路径:
首先,引入四个数组:
graph[Maxsize][Maxsize] : 用于创建图
d[i] : 源点到各个顶点的最短距离
visit[i] : 标记顶点i是否已经求得最短路径,1表示已求得,0表示未求得
p[i] : 源点到顶点i的最短路径上i的前一个顶点(前驱顶点)
第一步——创建图并初始化
(1)
#include <stdio.h>
#define INF 10000000
#define MAXSize 100// 最大顶点数
int graph[MAXSize][MAXSize];// 图的邻接矩阵
int main() {
int n, m;
printf("请输入顶点数和边数:(用空格分开它们)\n");
scanf_s("%d %d", &n, &m); // n:顶点数 m:边数
int u, v, w;
// u:边的起始点 v:边的终点 w:边的权重(即长度)
(2)图的初始化
// 初始化图的邻接矩阵
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if(i==j)
graph[i][j] = 0; //表示每一个顶点到自己的距离为0
else
graph[i][j] = INF; //表示每一个顶点都是单点,各不相连
}
}
| i | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 | INF | INF | INF | INF |
| 1 | INF | 0 | INF | INF | INF |
| 2 | INF | INF | 0 | INF | INF |
| 3 | INF | INF | INF | 0 | INF |
| 4 | INF | INF | INF | INF | 0 |
经过这一步后,图的样子:
再继续初始化:
printf("请分别输入边的起始点、终点、长度:(用空格分开它们)\n");
// 读入边的信息
for (int i = 0; i < m; i++)
{
scanf_s("%d %d %d", &u, &v,&w);
// u:边的起始点 v:边的终点 w:边的权重(即长度)
graph[u][v] = w;
graph[v][u] = w;
}
}
经过这一步后,邻接矩阵:
| i | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 0 | 0 | 3 | 7 | INF | INF |
| 1 | INF | 0 | 5 | INF | 9 |
| 2 | INF | INF | 0 | 5 | INF |
| 3 | INF | INF | INF | 0 | INF |
| 4 | INF | INF | INF | 6 | 0 |
经过这一步后,图的样子:
第二步
遍历除0外的所有其他点,确定原点0与各点i的直接距离d[i]。从而找出离原点0最近的点的最短路径
如图:
- d[0] = 0 , d[1] = 3 , d[2] = 7 , d[3] = INF(无穷大) , d[4] = INF
- visit = {1,0,0,0,0} (0点到0点本身的距离已经知道了,就是0,故visit[0]=1)
| i | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| d [ i ] | 0 | 3 | 7 | INF | INF |
| visit [ i ] | 1 | 0 | 0 | 0 | 0 |
| p [ i ] | 0 | 0 | 0 | 0 | 0 |
比较d[ i ] (i>0,因为原点本身不算在内)各值,找到点1最短路径为0->1,可得visit [1] =1。
第三步
又以已经找到最短路径的点(记为点target_index)为新的起始点(在本例中为点1),比较d[ i ]与d[ target_index ]+graph[ target_index ][ i ]
(graph[ target_index ][ i ]为新起始点到除前驱顶点以外的其他点的距离)
若d[i] > d[target_index] + graph[target_index][i],则更新源点0到顶点i的最短距离
重复这个算法直到每个最短路径被找到。
void dijkstra(int n) {
int count = 0;
//count是已经求得最短路径的顶点数
visit[0] = 1;
//这行代码表示源点0已经求得最短路径,其最短路径长度为0
p[0] = 0;
//意思是:源点0没有前驱点
count++;
//已求得最短路径的顶点数加1,这个顶点就是源点0
//初始化d[i]为源点0到顶点i的距离
for (int i = 1; i < n; i++)
{
d[i] = graph[0][i]; //源点0到顶点i的距离
p[i] = 0; //源点0到顶点i的最短路径上i的前一个顶点,就是源点0
}
//循环n-1次,每次求得一个顶点的最短路径
while (count<n)
{
int min = INF;
int target_index=-1 ; //target_index是下一个求得最短路径的顶点
//先找到离源点0最近的顶点
for (int i = 1; i < n; i++)
{
if (visit[i] == 0 && min > d[i])//找到一个离源点0最近的顶点
{
min= d[i];
target_index = i;
}
}
visit[target_index] = 1;//标记顶点target_index已经求得最短路径
count++;
//更新源点0到顶点i的最短距离
for (int i = 1; i < n; i++)
{
if (visit[i] == 0 && d[i] > d[target_index] + graph[target_index][i])
{
d[i] = d[target_index] + graph[target_index][i];
//更新源点0到顶点i的最短距离
p[i] = target_index;
//更新源点0到顶点i的最短路径上i的前一个顶点
}
}
}
}
三、完整代码
#include <stdio.h>
#define INF 10000000
#define MAXSize 100// 最大顶点数
int graph[MAXSize][MAXSize];
// 图的邻接矩阵
int d[MAXSize];
// d[i]为源点到各个顶点的最短距离
int visit[MAXSize];
// visit[i]标记顶点i是否已经求得最短路径
// 在这个数组中,1表示已求得最短路径,0表示未求得最短路径,相当于一个布尔数组
int p[MAXSize];
// p[i]为源点到顶点i的最短路径上i的前一个顶点
void dijkstra(int n) {
int count = 0;
//count是已经求得最短路径的顶点数
visit[0] = 1;
//这行代码表示源点0已经求得最短路径,其最短路径长度为0
p[0] = 0;
//意思是:源点0没有前驱点
count++;
//已求得最短路径的顶点数加1,这个顶点就是源点0
//初始化d[i]为源点0到顶点i的距离
for (int i = 1; i < n; i++)
{
d[i] = graph[0][i]; //源点0到顶点i的距离
p[i] = 0; //源点0到顶点i的最短路径上i的前一个顶点,就是源点0
}
//循环n-1次,每次求得一个顶点的最短路径
while (count<n)
{
int min = INF;
int target_index=-1 ; //target_index是下一个求得最短路径的顶点
//先找到离源点0最近的顶点
for (int i = 1; i < n; i++)
{
if (visit[i] == 0 && min > d[i])//找到一个离源点0最近的顶点
{
min= d[i];
target_index = i;
}
}
visit[target_index] = 1;//标记顶点target_index已经求得最短路径
count++;
//更新源点0到顶点i的最短距离
for (int i = 1; i < n; i++)
{
if (visit[i] == 0 && d[i] > d[target_index] + graph[target_index][i])
{
d[i] = d[target_index] + graph[target_index][i];
//更新源点0到顶点i的最短距离
p[i] = target_index;
//更新源点0到顶点i的最短路径上i的前一个顶点
}
}
}
}
void printDijkstra(int n) {
int path[MAXSize]; //path数组用来展示顶点0到顶点i的最短路径上的各顶点
for (int i = 1; i < n; i++)
{
if (d[i] == INF)
{
printf("顶点0到顶点%d没有最短路径\n", i);
}
else
{
printf("顶点0到顶点%d的长为%d最短路径为:",i,d[i]);
int cur = i;
int index = 0;
// 初始化当前节点(cur)为目标节点i,
// 并设置路径数组(path)的索引(index)为0。
path[index] = cur; //将终点cur加入到path数组中
//这个循环的目的是沿着最短路径从目标节点i回溯到源点(顶点0)
while (1)
{
path[index+1]= p[path[index]];
// 找到当前节点的前驱节点,并将其添加到路径数组(path)中
if (path[index + 1] == 0)
//条件判断是检查是否已经回溯到源点(顶点0)
{
break;
}
index++;//更新路径数组(path)的索引(index)
}
// 这个for循环用来打印出从源点(顶点0)到目标节点i的最短路径
for (int j = index+1; j > 0; j--)
{
printf("%d->",path[j]);
// 这行代码打印出路径中各节点。
// 注意,由于路径是从目标节点i回溯到源点(顶点0)得到的,
// 所以打印路径时需要从后往前打印
}
printf("%d\n",path[0]);
// 这行代码是打印出路径的最后一个节点(也就是目标节点i)
}
}
}
int main() {
int n, m;
printf("请输入顶点数和边数:(用空格分开它们)\n");
scanf_s("%d %d", &n, &m); // n:顶点数 m:边数
int u, v, w;
// u:边的起始点 v:边的终点 w:边的权重(即长度)(必须非负)
// 初始化图的邻接矩阵
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if(i==j)
graph[i][j] = 0; //表示每一个顶点到自己的距离为0
else
graph[i][j] = INF; //表示每一个顶点都是单点,各不相连
}
}
printf("请分别输入边的起始点、终点、长度:(用空格分开它们)\n");
// 读入边的信息
for (int i = 0; i < m; i++)
{
scanf_s("%d %d %d", &u, &v,&w);
// u:边的起始点 v:边的终点 w:边的权重(即长度)
graph[u][v] = w;
graph[v][u] = w;
}
dijkstra(n);
printDijkstra(n);
}
输入输出范例:
请输入顶点数和边数:(用空格分开它们)
(输入) 5 6
请分别输入边的起始点、终点、长度:(用空格分开它们)
(输入)
0 1 3
0 2 7
1 2 5
1 4 9
2 3 5
4 3 6
(输出)
顶点0到顶点1的长为3最短路径为:0->1
顶点0到顶点2的长为7最短路径为:0->2
顶点0到顶点3的长为12最短路径为:0->2->3
顶点0到顶点4的长为12最短路径为:0->1->4