数据结构7.2:Prim算法与Dijkstra 算法

文章介绍了Prim算法和Dijkstra算法在解决带权图问题上的应用,Prim算法主要用于构建无向图的最小生成树,而Dijkstra算法则用于找出单源最短路径。文中提供了两种算法的C语言实现,并展示了运行结果,证明了算法的有效性。
摘要由CSDN通过智能技术生成

没看懂老师写的,学习其他人的代码。

一、代码

先上代码,后说废话:

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算法每次选择的顶点都是距离起点最近的一个点,并更新该点到其邻接顶点的距离。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值