基于邻接矩阵的Dijkstra算法

通过Dijkstra算法,求两点间最短路径及长度。

输入样例说明:
3 3//第一组输入的顶点数和边数
A B C//三个顶点
A B 1//从A到B有权值为1的边,以此类推
B C 1
A C 3
A C//求A到C的最短路径
···//其余输入
0 0//输入结束

输入样例:
3 3
A B C
A B 1
B C 1
A C 3
A C
6 8
A B C D E F
A F 100
A E 30
A C 10
B C 5
C D 50
E D 20
E F 60
D F 10
A F
5 6
A B C D E
A B 5
A E 50
B E 10
A D 10
D C 10
E C 10
A C
0 0

输出样例:
2
A B C
60
A E D F
20
A D C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MaxVertexNum 10
#define MaxInt 999

typedef struct MGraph{
    char vex[MaxVertexNum];
    int edge[MaxVertexNum][MaxVertexNum];
    int vexNum, edgeNum;
} MGraph;/*ˈmeɪtrɪks*/

void GraphInit(MGraph *G);
void GraphShow(MGraph G);
void addEdge(MGraph *G, char src, char des, int weight);
int Dijkstra(MGraph G, int srcPos, int desPos, char *path);

int main() {
    MGraph G;

    while (scanf("%d %d%*c", &G.vexNum, &G.edgeNum) && G.vexNum != 0) {
        char src, des;//源点和目标点
        int srcPos, desPos;//源点和目标点的位序
        int weight;//边的权值
        char path[MaxVertexNum] = "";//最短路径

        /*构造无向图*/
        GraphInit(&G);
        for (int i = 0; i < G.vexNum; i++) scanf("%c%*c", G.vex + i);
        for (int i = 0; i < G.edgeNum; i++) {
            scanf("%c %c %d%*c", &src, &des, &weight);
            addEdge(&G, src, des, weight);
        }

        /*Dijkstra求最短路径*/
        scanf("%c %c%*c", &src, &des);
        for (int i = 0; i < G.vexNum; i++) {
            /*将源目结点转化为对应位序*/
            if (G.vex[i] == src) srcPos = i;
            if (G.vex[i] == des) desPos = i;
        }
        printf("%d\n", Dijkstra(G, srcPos, desPos, path));
        for (int i = strlen(path) - 1; i >= 0; i--) {
            printf("%c%c", path[i], !i ? 10 : 32);//反向输出
        }
    }

    return 0;
}

int Dijkstra(MGraph G, int srcPos, int desPos, char *path) {
    int dist[MaxVertexNum];//每个结点与源点的最短路径长度,记为dist
    int final[MaxVertexNum];//标记每个结点是否已经访问,已访问的结点记为集合S
    int lastAlter[MaxVertexNum];//最后一次修改当前结点dist值的结点的下标,用于回溯路径

    /*初始化*/
    for (int i = 0; i < G.vexNum; i++) {
        lastAlter[i] = srcPos;//从目标结点回溯到源点的路径恰好为path,所以回溯结束的标志就是访问到了源点
        dist[i] = G.edge[srcPos][i];//初始值就是源点到各个结点的弧的长度
        final[i] = 0;//初始时每个结点都没访问
    }
    final[srcPos] = 1;//源点默认已经被访问

    /*主循环*/
    for (int i = 0; i < G.vexNum; i++) {
        int minDis = MaxInt;//最短路径长度
        int minPos = -1;//最短路径对应结点

        /*选出一个不在集合S中且距离源点最近的结点*/
        for (int j = 0; j < G.vexNum; j++) {
            if (!final[j]) {
                if (dist[j] < minDis) {
                    /*更新最短路径长度和对应结点*/
                    minDis = dist[j];
                    minPos = j;
                }
            }
        }

        final[minPos] = 1;//将当前最短路径对应的结点加入集合S
        if (minPos == desPos) break;//已经把目标结点加入了集合S

        /*更新最短路径和距离*/
        for (int j = 0; j < G.vexNum; j++) {
            //如果当前结点不在S中,且加入了新结点后当前结点与源点的距离减小,更新距离
            if (!final[j] && minDis + G.edge[minPos][j] < dist[j]) {
                dist[j] = minDis + G.edge[minPos][j];//更新距离
                lastAlter[j] = minPos;//G.vex[minPos]结点修改了G.vex[j]结点的dist值
            }
        }
    }

    /*通过lastAlter回溯得到路径path*/
    int node = desPos, i = 0;
    while (node != srcPos) {
        path[i++] = G.vex[node];
        node = lastAlter[node];
    }
    path[i] = G.vex[srcPos];

    return dist[desPos];
}

void GraphInit(MGraph *G) {
    for (int i = 0; i < G->vexNum; i++) {
        for (int j = 0; j < G->vexNum; j++) {
            G->edge[i][j] = MaxInt;
        }
    }
}

void GraphShow(MGraph G) {
    printf("  ");
    for (int i = 0; i < G.vexNum; i++) {
        printf("%3c%c", G.vex[i], i == G.vexNum - 1 ? 10 : 32);
    }
    for (int i = 0; i < G.vexNum; i++) {
        printf("%c ", G.vex[i]);
        for (int j = 0; j < G.vexNum; j++) {
            if (G.edge[i][j] == MaxInt) {
                printf("%3c%c", 'x', j == G.vexNum - 1 ? 10 : 32);
            } else {
                printf("%3d%c", G.edge[i][j], j == G.vexNum - 1 ? 10 : 32);
            }
        }
    }
}

void addEdge(MGraph *G, char src, char des, int weight) {
    int i = 0, j = 0;

    while (G->vex[i] != src) i++;
    while (G->vex[j] != des) j++;
    G->edge[i][j] = G->edge[j][i] = weight;
}
Dijkstra算法是一种用于求解单源最短路径的经典算法,可以使用邻接矩阵来实现。具体步骤如下: 1. 初始化距离数组dist和标记数组visited,将源点s的距离设为0,其他点的距离设为无穷大,所有点的visited值均为false。 2. 从距离数组dist中选择一个未标记的距离最小的点u,标记该点,并对其所有邻接点v进行松弛操作:如果从源点s到u再到v的距离比从源点s直接到v的距离更短,则更新v的距离dist[v]为新的距离,并将v标记为未访问。 3. 重复步骤2,直到所有节点均被标记为已访问,或者不存在未标记节点的距离小于无穷大的点。 4. 最终距离数组dist中存储了从源点s到所有其他点的最短距离。 下面给出使用邻接矩阵实现Dijkstra算法的代码示例: ```python import sys # 用邻接矩阵表示图 class Graph: def __init__(self, vertices): self.V = vertices self.graph = [[0 for column in range(vertices)] for row in range(vertices)] # 找到距离最小的未访问节点 def min_distance(self, dist, visited): min_dist = sys.maxsize min_index = -1 for v in range(self.V): if dist[v] < min_dist and not visited[v]: min_dist = dist[v] min_index = v return min_index # 打印最短路径 def print_path(self, parent, j): if parent[j] == -1: print(j, end=' ') return self.print_path(parent, parent[j]) print(j, end=' ') # Dijkstra算法 def dijkstra(self, src): dist = [sys.maxsize] * self.V parent = [-1] * self.V dist[src] = 0 visited = [False] * self.V for cout in range(self.V): u = self.min_distance(dist, visited) visited[u] = True for v in range(self.V): if self.graph[u][v] > 0 and not visited[v] and \ dist[u] + self.graph[u][v] < dist[v]: dist[v] = dist[u] + self.graph[u][v] parent[v] = u print("最短路径:") for i in range(self.V): print(src, "->", i, "距离:", dist[i], "路径:", end=' ') self.print_path(parent, i) print() # 测试 g = Graph(9) g.graph = [[0, 4, 0, 0, 0, 0, 0, 8, 0], [4, 0, 8, 0, 0, 0, 0, 11, 0], [0, 8, 0, 7, 0, 4, 0, 0, 2], [0, 0, 7, 0, 9, 14, 0, 0, 0], [0, 0, 0, 9, 0, 10, 0, 0, 0], [0, 0, 4, 14, 10, 0, 2, 0, 0], [0, 0, 0, 0, 0, 2, 0, 1, 6], [8, 11, 0, 0, 0, 0, 1, 0, 7], [0, 0, 2, 0, 0, 0, 6, 7, 0]] g.dijkstra(0) ``` 上述代码中,我们使用邻接矩阵来表示图,dist数组存储源点到各个节点的距离,parent数组存储最短路径上每个节点的前驱节点,visited数组表示该节点是否被访问过。在Dijkstra算法中,我们通过找到距离最小的未访问节点,以及对其邻接点进行松弛操作,来逐步求解源点到各个节点的最短距离。最后打印出最短路径和距离即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值