一、最短路径
在网图和非网图中,最短路径的含义是不同的。由于非网图没有边上的权值,所谓最短路径,其实指的就是两个顶点之间经过的边数最少的路劲(即可以理解为把每一条边的权值看作是1)。
对于网图来说,最短路径,是指两顶点之间经过的边上的权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。
求带权有向图G的最短路径问题一般可分为两类:一是单源最短路径,即求图中某一个顶点到其它顶点的最短路径,可以通过经典的 Dijkstra(迪杰斯特拉)算法求解(即是我要介绍的算法);二是求每对顶点间的最短路径,可通过Floyd(弗洛伊德)算法来求解。
二、Dijkstra算法
具体步骤如下:
1. 初始化距离数组,将起始节点的距离设为0,其他节点的距离设为无穷大。
2. 选择当前距离最小的节点作为当前节点。
3. 更新当前节点的相邻节点的距离,若通过当前节点可以找到更短的路径,则更新距离数组中相应节点的距离。
4. 标记当前节点为已访问。
5. 重复步骤2-4,直到所有节点都被标记为已访问。
6. 根据距离数组得到最短路径。
Dijkstra算法是一种贪心算法,它保证每次选取的节点都是当前距离最小的节点。该算法在解决无负权边的图的最短路径问题上非常有效,但对于包含负权边的图则不适用。
#include<stdio.h>
#include<limits.h>
#define V 8
- 头文件<limits.h>包含了INT _MAX和INT_MIN等特定整数值
- 定义顶点数为8
int minDistance(int dist[], int sptSet[]) {
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++) {
if (sptSet[v] == 0 && dist[v] <= min) {
min = dist[v];
min_index = v;
}
}
return min_index;
}
int minDistance(int dist[], int sptSet[])
:这是一个函数声明,它接受两个参数:dist
数组用于存储从源点到各个顶点的距离,sptSet
数组用于标记顶点是否已经纳入最短路径树中。int min = INT_MAX, min_index;
:首先声明了一个min
变量,用于存储当前最小的距离值,初始化为整型变量的最大值。另外还声明了min_index
变量,用于存储当前最小距离值的顶点下标。for (int v = 0; v < V; v++)
:这是一个循环,遍历所有的顶点。if (sptSet[v] == 0 && dist[v] <= min)
:在循环中,首先检查顶点是否已经纳入最短路径树中且到源点的距离是否小于等于当前的最小距离值。min = dist[v];
:如果上述条件满足,更新min
为当前距离值,表示找到了更小的距离。min_index = v;
:同时更新min_index
为当前顶点的下标。return min_index;
:返回具有最小距离值的顶点的下标。
因此,这段代码的目的是在未纳入最短路径树的顶点中找到距禒源点最近的顶点(即距离值最小的顶点)的下标,并将其作为返回值。
void printPath(int parent[], int j) {
if (parent[j] == -1) {
printf("%d", j);
return;
}
printPath(parent, parent[j]);
printf(" ->% d ", j);
}
void printPath(int parent[], int j)
:这是一个函数声明,它接受两个参数:parent
数组用于存储最短路径树中每个顶点的父节点,j
表示当前正在处理的顶点。if (parent[j] == -1)
:这是一个条件判断,如果当前顶点的父节点为 -1,表示已经回溯到了源点,直接打印当前顶点并返回。printf("%d", j)
:打印当前顶点的值。return
:函数结束,完成打印。printPath(parent, parent[j])
:如果当前顶点的父节点不是 -1,就递归地调用printPath
函数,打印从源点到当前顶点的路径。printf(" ->% d ", j)
:打印箭头连接当前顶点的值。
因此,这段代码的作用是递归地打印从源点到目标顶点的最短路径,路径上的每个顶点都按照箭头连接的形式进行打印。
void printSolution(int dist[], int parent[], int src) {
printf("Vertex \t Distance from Source \t Path\n");
for (int i = 0; i < V; i++) {
printf("%d \t\t %d \t\t ", i, dist[i]);
printPath(parent, i);
printf("\n");
}
}
void dijkstra(int graph[V][V], int src) {
int dist[V];
int parent[V];
int sptSet[V];
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
sptSet[i] = 0;
}
dist[src] = 0;
parent[src] = -1;
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet);
sptSet[u] = 1;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
parent[v] = u;
}
}
printf("Iteration %d:\n", count + 1);
printSolution(dist, parent, src);
}
printf("Final Result:\n");
printSolution(dist, parent, src);
}
注释:
void dijkstra(int graph[V][V], int src) {
int dist[V]; // 用于存储从源点到各个顶点的距离
int parent[V]; // 用于存储最短路径树中每个顶点的父节点
int sptSet[V]; // 用于标记顶点是否包含在最短路径树中
// 初始化距离数组和最短路径树标记数组
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
sptSet[i] = 0;
}
dist[src] = 0; // 设置源点到自身的距离为0
parent[src] = -1; // 源点的父节点设置为-1,表示没有父节点
// 进行 V-1 次迭代
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet); // 在未纳入最短路径树的顶点中找到距禒源点最近的顶点
sptSet[u] = 1; // 将该顶点标记为已纳入最短路径树
// 更新从源点出发到未纳入最短路径树的顶点的距离值和父节点
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
parent[v] = u;
}
}
printf("Iteration %d:\n", count + 1);
printSolution(dist, parent, src); // 打印每次迭代的结果
}
printf("Final Result:\n");
printSolution(dist, parent, src); // 打印最终结果
}
这段代码的作用是执行 Dijkstra 算法,计算源点到各顶点的最短路径,并输出每次迭代的结果以及最终结果。在每次迭代中,都会找到离源点最近的顶点加入到最短路径树中,并更新其他顶点到源点的最短距离和父节点。
最后,在算法执行完毕后,将打印出最终的结果,包括每个顶点到源点的最短距离以及相应
的最短路径。
最后是主函数:
int main() {
int graph[V][V] = {{0, 10, 1000, 1000, 15, 20, 1000, 1000},
{1000, 0, 25, 1000, 1000, 1000, 1000, 1000},
{1000, 1000, 0, 20, 1000, 1000, 1000, 1000},
{1000, 1000, 1000, 0, 1000, 1000, 1000, 1000},
{1000, 1000, 1000, 1000, 0, 5, 1000, 1000},
{1000, 6, 1000, 1000, 1000, 0, 7, 1000},
{1000, 1000, 8, 10, 1000, 1000, 0, 4},
{1000, 1000, 1000, 5, 1000, 1000, 1000, 0}};
dijkstra(graph, 0);
return 0;
}
int src 是源顶点
运行结果如下:
源代码附上:
#include <stdio.h>
#include <limits.h>
#define V 8
int minDistance(int dist[], int sptSet[]) {
int min = INT_MAX, min_index;
for (int v = 0; v < V; v++) {
if (sptSet[v] == 0 && dist[v] <= min) {
min = dist[v];
min_index = v;
}
}
return min_index;
}
void printPath(int parent[], int j) {
if (parent[j] == -1) {
printf("%d", j);
return;
}
printPath(parent, parent[j]);
printf(" ->% d ", j);
}
void printSolution(int dist[], int parent[], int src) {
printf("Vertex \t Distance from Source \t Path\n");
for (int i = 0; i < V; i++) {
printf("%d \t\t %d \t\t ", i, dist[i]);
printPath(parent, i);
printf("\n");
}
}
void dijkstra(int graph[V][V], int src) {
int dist[V];
int parent[V];
int sptSet[V];
for (int i = 0; i < V; i++) {
dist[i] = INT_MAX;
sptSet[i] = 0;
}
dist[src] = 0;
parent[src] = -1;
for (int count = 0; count < V - 1; count++) {
int u = minDistance(dist, sptSet);
sptSet[u] = 1;
for (int v = 0; v < V; v++) {
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
parent[v] = u;
}
}
printf("Iteration %d:\n", count + 1);
printSolution(dist, parent, src);
}
printf("Final Result:\n");
printSolution(dist, parent, src);
}
int main() {
int graph[V][V] = {{0, 10, 1000, 1000, 15, 20, 1000, 1000},
{1000, 0, 25, 1000, 1000, 1000, 1000, 1000},
{1000, 1000, 0, 20, 1000, 1000, 1000, 1000},
{1000, 1000, 1000, 0, 1000, 1000, 1000, 1000},
{1000, 1000, 1000, 1000, 0, 5, 1000, 1000},
{1000, 6, 1000, 1000, 1000, 0, 7, 1000},
{1000, 1000, 8, 10, 1000, 1000, 0, 4},
{1000, 1000, 1000, 5, 1000, 1000, 1000, 0}};
dijkstra(graph, 0);
return 0;
}