没看懂老师写的,学习其他人的代码。
一、代码
先上代码,后说废话:
1.prim算法的代码实现:
#include <stdio.h>
#include <stdlib.h>
/**
* 图顶点之前不通,那么邻接矩阵的值为MAX
* 如果顶点是自己本身,那么值为0
*/
#define MAX 32767
typedef struct Graph {
char* vexs;
int** arcs;
int vexNum;
int arcNum;
}Graph;
typedef struct Edge {
char vex;
int weight;
}Edge;
/**
* 当edge.weight = 0时,代表顶点加入到U集合中
*/
Edge* initEdeg(Graph* G, int index) {
Edge* edge = (Edge*)malloc(sizeof(Edge) * G -> vexNum);
for (int i = 0; i < G ->vexNum; i++) {
edge[i].vex = G -> vexs[index];
edge[i].weight = G -> arcs[index][i];
}
return edge;
}
int getMinEdge(Edge* edge, Graph* G) {
int index;
int min = MAX;
for (int i = 0; i < G -> vexNum; i++) {
if (edge[i].weight != 0 && min > edge[i].weight) {
min = edge[i].weight;
index = i;
}
}
return index;
}
void prim(Graph* G, int index) {
int min;
Edge* edge = initEdeg(G, index);
for (int i = 0; i < G -> vexNum - 1; i++) {
min = getMinEdge(edge, G);
printf("v%c --> v%c, weight = %d\n", edge[min].vex, G -> vexs[min], edge[min].weight);
edge[min].weight = 0;
for (int j = 0; j < G -> vexNum; j++) {
if (G -> arcs[min][j] < edge[j].weight) {
edge[j].weight = G -> arcs[min][j];
edge[j].vex = G -> vexs[min];
}
}
}
}
Graph* initGraph(int vexNum) {
Graph* G = (Graph*)malloc(sizeof(Graph));
G -> vexs = (char*)malloc(sizeof(char) * vexNum);
G -> arcs = (int**)malloc(sizeof(int*) * vexNum);
for (int i = 0 ; i < vexNum; i++) {
G -> arcs[i] = (int*)malloc(sizeof(int) * vexNum);
}
G -> vexNum = vexNum;
G -> arcNum = 0;
return G;
}
void createGraph(Graph* G, char* vexs, int* arcs) {
for (int i = 0 ; i < G -> vexNum; i++) {
G -> vexs[i] = vexs[i];
for (int j = 0; j < G -> vexNum; j++) {
G -> arcs[i][j] = *(arcs + i * G -> vexNum + j);
if (G -> arcs[i][j] != 0 && G -> arcs[i][j] != MAX)
G -> arcNum ++;
}
}
G -> arcNum /= 2;
}
void DFS(Graph* G, int* visited, int index) {
printf("%c\t", G -> vexs[index]);
visited[index] = 1;
for (int i = 0; i < G ->vexNum; i++) {
if (G -> arcs[index][i] > 0 && G -> arcs[index][i] != MAX && !visited[i]) {
DFS(G, visited, i);
}
}
}
int main() {
Graph* G = initGraph(6);
int* visited = (int*)malloc(sizeof(int) * G -> vexNum);
for (int i = 0; i < G -> vexNum; i++)
visited[i] = 0;
int arcs[6][6] = {
0, 6, 1, 5, MAX, MAX,
6, 0, 5, MAX, 3, MAX,
1, 5, 0, 5, 6, 4,
5, MAX, 5, 0, MAX, 2,
MAX, 3, 6, MAX, 0, 6,
MAX, MAX, 4, 2, 6, 0
};
createGraph(G, "123456", (int*)arcs);
DFS(G, visited, 0);
printf("\n");
prim(G, 0);
return 0;
}
2.Dijkstra 算法的代码实现 (算法来源http://t.csdn.cn/YiKle)(写得太好了)
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define MaxVerterNum 100 // 顶点数目的最大值
#define INFINITY 65535 // 用65535代表 ∞
typedef char VertexType; // 顶点的数据类型
typedef int EdgeType; // 带权图中边上权值的数据类型
/* 邻接矩阵的存储结构 */
typedef struct
{
VertexType Vexs[MaxVerterNum]; // 顶点表
EdgeType Edge[MaxVerterNum][MaxVerterNum]; // 邻接矩阵
int vexNum, arcNum; // 图当前顶点数和弧数
}MGraph;
/*清除缓冲区的换行符*/
void Clean(void)
{
while (getchar() != '\n')
continue;
}
/* 建立无向网图的邻接矩阵表示 */
void CreateMGraph(MGraph* G);
/* 迪杰斯特拉(Dijkstra) 算法*/
typedef int Patharc[MaxVerterNum]; // 用于存储最短路径下标的数组,从源点Vi到顶点Vj之间的最短路径的前驱
typedef int ShortPathTable[MaxVerterNum]; // 用于存储到各点最短路径的权值和
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc path, ShortPathTable D);
/* 输出最短路径 */
/* Dijkstra算法的结果输出 */
void Show_ShortestPath_Dijkstra(Patharc path, ShortPathTable dist, MGraph G, int v0);
int main(void)
{
MGraph G;
Patharc path;
ShortPathTable dist;
CreateMGraph(&G);
for (int i = 0; i < G.vexNum; i++) // 输出各点到各点的最短路径序列,不再局限于一个顶点
{
ShortestPath_Dijkstra(G, i, path, dist);
Show_ShortestPath_Dijkstra(path, dist, G, i);
}
return 0;
}
/* 建立无向网图的邻接矩阵表示 */
void CreateMGraph(MGraph* G)
{
int i, j, k, w;
printf("请输入顶点数和边数:");
scanf("%d %d", &G->vexNum, &G->arcNum); // 获取无向图顶点数和边数
printf("请输入全部顶点信息:\n");
Clean(); // 将换行符去除
for (i = 0; i < G->vexNum; i++) // 读取顶点信息,建立顶点表
scanf("%c", &G->Vexs[i]);
for (i = 0; i < G->vexNum; i++)
for (j = 0; j < G->vexNum; j++)
G->Edge[i][j] = INFINITY; // 邻接矩阵初始化
for (k = 0; k < G->arcNum; k++) // 读入arcNum条边,建立邻接矩阵
{
printf("请输入边(Vi, Vj)上的下标i,下标j和权w:\n");
scanf("%d %d %d", &i, &j, &w); // 获取边和权
G->Edge[i][j] = w; // 无向图矩阵对称
G->Edge[j][i] = G->Edge[i][j];
}
return;
}
/* 迪杰斯特拉(Dijkstra) 算法*/
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc path, ShortPathTable dist)
{
int v, w, k, min;
int final[MaxVerterNum]; /* final[w] = 1表示求得顶点 v0 至 vw的最短路 径,即已访问过顶点vw*/
for (v = 0; v < G.vexNum; v++)
{
final[v] = 0; // 全部顶点初始化为未知最短路径状态
dist[v] = G.Edge[v0][v]; // 将与v0点有连线的顶点加上权值
path[v] = -1; // 初始化路劲数组p为-1
}
dist[v0] = 0; // v0至v0路径为0
final[v0] = 1; // v0至v0不需要路径
/* 开始主循环,每次求得v0到某个顶点v的最短路径*/
for (v = 1; v < G.vexNum; v++)
{
min = INFINITY; // 当前所知离v0顶点的最近距离
for (w = 0; w < G.vexNum; w++) // 寻找离v0最近的顶点
{
if (!final[w] && dist[w] < min)
{
k = w;
min = dist[w]; // w顶点离v0顶点更近
}
}
final[k] = 1; // 将目前找到的最近的顶点置为1
for (w = 0; w < G.vexNum; w++) // 修正当前最短路径及距离
{
/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
if (!final[w] && (min + G.Edge[k][w] < dist[w]))
{
/* 找到了更短的路径,修改D[w]和P[w] */
dist[w] = min + G.Edge[k][w]; // 修改当前路径长度
path[w] = k;
}
}
}
}
/* 输出最短路径 */
/* Dijkstra算法的结果输出 */
void Show_ShortestPath_Dijkstra(Patharc path, ShortPathTable dist, MGraph G, int v)
{
int w, k;
printf("V%d到各点的最短路径如下:\n", v);
for (w = 0; w < G.vexNum; w++)
{
if (w != v)
{
printf("V%d-V%d weight: %d", v, w, dist[w]);
k = path[w];
printf(" path: V%d", w);
while (k != -1) // 当 k = -1 ,结束循环并输出源点
{
printf(" <- V%d", k);
k = path[k];
}
printf(" <- V%d\n", v);
}
}
printf("\n");
}
二、运行结果:
1.prim算法
1 2 3 4 6 5
v1 --> v3, weight = 1
v3 --> v6, weight = 4
v6 --> v4, weight = 2
v3 --> v2, weight = 5
v2 --> v5, weight = 3
D:\visual studio\test.c\x64\Debug\test.c.exe (进程 9048)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
2.Dijkstra 算法
请输入顶点数和边数:7 12
请输入全部顶点信息:
01234567
请输入边(Vi, Vj)上的下标i,下标j和权w:
0 1 6
请输入边(Vi, Vj)上的下标i,下标j和权w:
0 2 3
请输入边(Vi, Vj)上的下标i,下标j和权w:
1 2 2
请输入边(Vi, Vj)上的下标i,下标j和权w:
1 3 1
请输入边(Vi, Vj)上的下标i,下标j和权w:
1 4 4
请输入边(Vi, Vj)上的下标i,下标j和权w:
2 3 5
请输入边(Vi, Vj)上的下标i,下标j和权w:
2 5 7
请输入边(Vi, Vj)上的下标i,下标j和权w:
3 4 3
请输入边(Vi, Vj)上的下标i,下标j和权w:
3 5 6
请输入边(Vi, Vj)上的下标i,下标j和权w:
4 5 2
请输入边(Vi, Vj)上的下标i,下标j和权w:
4 6 2
请输入边(Vi, Vj)上的下标i,下标j和权w:
5 6 3
V0到各点的最短路径如下:
V0-V1 weight: 16 path: V1 <- V2 <- V4 <- V6 <- V0
V0-V2 weight: 13 path: V2 <- V4 <- V6 <- V0
V0-V3 weight: 18 path: V3 <- V1 <- V2 <- V4 <- V6 <- V0
V0-V4 weight: 7 path: V4 <- V6 <- V0
V0-V5 weight: 19 path: V5 <- V2 <- V4 <- V6 <- V0
V0-V6 weight: 2 path: V6 <- V0
V1到各点的最短路径如下:
V1-V0 weight: 16 path: V0 <- V6 <- V4 <- V2 <- V1
V1-V2 weight: 3 path: V2 <- V1
V1-V3 weight: 2 path: V3 <- V1
V1-V4 weight: 9 path: V4 <- V2 <- V1
V1-V5 weight: 9 path: V5 <- V2 <- V1
V1-V6 weight: 14 path: V6 <- V4 <- V2 <- V1
V2到各点的最短路径如下:
V2-V0 weight: 13 path: V0 <- V6 <- V4 <- V2
V2-V1 weight: 3 path: V1 <- V2
V2-V3 weight: 5 path: V3 <- V1 <- V2
V2-V4 weight: 6 path: V4 <- V2
V2-V5 weight: 6 path: V5 <- V2
V2-V6 weight: 11 path: V6 <- V4 <- V2
V3到各点的最短路径如下:
V3-V0 weight: 18 path: V0 <- V6 <- V4 <- V2 <- V1 <- V3
V3-V1 weight: 2 path: V1 <- V3
V3-V2 weight: 5 path: V2 <- V1 <- V3
V3-V4 weight: 11 path: V4 <- V2 <- V1 <- V3
V3-V5 weight: 11 path: V5 <- V2 <- V1 <- V3
V3-V6 weight: 16 path: V6 <- V4 <- V2 <- V1 <- V3
V4到各点的最短路径如下:
V4-V0 weight: 7 path: V0 <- V6 <- V4
V4-V1 weight: 9 path: V1 <- V2 <- V4
V4-V2 weight: 6 path: V2 <- V4
V4-V3 weight: 11 path: V3 <- V1 <- V2 <- V4
V4-V5 weight: 12 path: V5 <- V2 <- V4
V4-V6 weight: 5 path: V6 <- V4
V5到各点的最短路径如下:
V5-V0 weight: 19 path: V0 <- V6 <- V4 <- V2 <- V5
V5-V1 weight: 9 path: V1 <- V2 <- V5
V5-V2 weight: 6 path: V2 <- V5
V5-V3 weight: 11 path: V3 <- V1 <- V2 <- V5
V5-V4 weight: 12 path: V4 <- V2 <- V5
V5-V6 weight: 17 path: V6 <- V4 <- V2 <- V5
V6到各点的最短路径如下:
V6-V0 weight: 2 path: V0 <- V6
V6-V1 weight: 14 path: V1 <- V2 <- V4 <- V6
V6-V2 weight: 11 path: V2 <- V4 <- V6
V6-V3 weight: 16 path: V3 <- V1 <- V2 <- V4 <- V6
V6-V4 weight: 5 path: V4 <- V6
V6-V5 weight: 17 path: V5 <- V2 <- V4 <- V6
D:\visual studio\test.c\x64\Debug\test.c.exe (进程 24660)已退出,代码为 0。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .
三、分析:
Prim法和Dijkstra算法都是解决带权无向图的最短路径问题,但两者的应用场景不同。
1. Prim算法
Prim算法用于求解无向带权连通图的最小生成树,即从图中选取一部分边和所有顶点形成的子图,使得这个子图是一棵树,且边的权重之和最小。Prim算法的基本思路是从一个顶点出发,每次选取一条权重最小的边连接该点和不在树中的点,直到所有的顶点都在树中。
具体步骤:
- 首先选择一个起始顶点,将起始顶点加入到U集合中,其他点加入到V集合中。
- 在V集合中选择一条到U集合距离最短的边,将其加入到U集合中,并将该边记录为最小生成树上的一条边。
- 重复第二步,直到所有的顶点都在U集合中为止。
2. Dijkstra算法(算法来源 http://t.csdn.cn/YiKle)
Dijkstra算法用于解决有向带权图的单源最短路径问题,即从一个源点到其他所有点的最短路径。该算法基于贪心思想,每次选取当前未访问的距离起点最近的一个顶点,并更新该顶点到其邻接顶点的距离,直到所有顶点都被访问。
具体步骤:
- 首先确定一个起点,把该点加入到已知最短路径的集合S中,其他点加入到未知的集合V中。
- 对于V中的每个点,计算其到起点的最短距离并记录下来,如果存在一条更短的路径,就更新该点的最短距离,直到所有的点都被访问。
- 重复第二步直到所有点的最短路径都被确定。
简单来说,Prim算法求的是最小生成树,而Dijkstra算法求的是单源最短路径。两者的不同在于Prim算法在每次选择边和顶点时只考虑该点和不在树中的点之间的边,而Dijkstra算法每次选择的顶点都是距离起点最近的一个点,并更新该点到其邻接顶点的距离。