Dijkstra算法(一)之 C语言详解
来源:互联网 发布:邮差包推荐 知乎 编辑:程序博客网 时间:2024/05/20 03:39
出自:http://www.cnblogs.com/skywang12345/p/3711512.html
本章介绍迪杰斯特拉算法。和以往一样,本文会先对迪杰斯特拉算法的理论论知识进行介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现。
目录
1. 迪杰斯特拉算法介绍
2. 迪杰斯特拉算法图解
3. 迪杰斯特拉算法的代码说明
4. 迪杰斯特拉算法的源码转载请注明出处:http://www.cnblogs.com/skywang12345/
更多内容:数据结构与算法系列 目录
迪杰斯特拉算法介绍
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
基本思想
通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。
此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。
初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。
操作步骤
(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。
(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
(4) 重复步骤(2)和(3),直到遍历完所有顶点。
单纯的看上面的理论可能比较难以理解,下面通过实例来对该算法进行说明。
迪杰斯特拉算法图解
以上图G4为例,来对迪杰斯特拉进行算法演示(以第4个顶点D为起点)。
初始状态:S是已计算出最短路径的顶点集合,U是未计算除最短路径的顶点的集合!
第1步:将顶点D加入到S中。
此时,S={D(0)}, U={A(∞),B(∞),C(3),E(4),F(∞),G(∞)}。 注:C(3)表示C到起点D的距离是3。
第2步:将顶点C加入到S中。
上一步操作之后,U中顶点C到起点D的距离最短;因此,将C加入到S中,同时更新U中顶点的距离。以顶点F为例,之前F到D的距离为∞;但是将C加入到S之后,F到D的距离为9=(F,C)+(C,D)。
此时,S={D(0),C(3)}, U={A(∞),B(23),E(4),F(9),G(∞)}。
第3步:将顶点E加入到S中。
上一步操作之后,U中顶点E到起点D的距离最短;因此,将E加入到S中,同时更新U中顶点的距离。还是以顶点F为例,之前F到D的距离为9;但是将E加入到S之后,F到D的距离为6=(F,E)+(E,D)。
此时,S={D(0),C(3),E(4)}, U={A(∞),B(23),F(6),G(12)}。
第4步:将顶点F加入到S中。
此时,S={D(0),C(3),E(4),F(6)}, U={A(22),B(13),G(12)}。
第5步:将顶点G加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12)}, U={A(22),B(13)}。
第6步:将顶点B加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13)}, U={A(22)}。
第7步:将顶点A加入到S中。
此时,S={D(0),C(3),E(4),F(6),G(12),B(13),A(22)}。
此时,起点D到各个顶点的最短距离就计算出来了:A(22) B(13) C(3) D(0) E(4) F(6) G(12)。
迪杰斯特拉算法的代码说明
以"邻接矩阵"为例对迪杰斯特拉算法进行说明,对于"邻接表"实现的图在后面会给出相应的源码。
1. 基本定义
// 邻接矩阵typedef struct _graph{ char vexs[MAX]; // 顶点集合 int vexnum; // 顶点数 int edgnum; // 边数 int matrix[MAX][MAX]; // 邻接矩阵}Graph, *PGraph;// 边的结构体typedef struct _EdgeData{ char start; // 边的起点 char end; // 边的终点 int weight; // 边的权重}EData;
Graph是邻接矩阵对应的结构体。
vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示"顶点i(即vexs[i])"和"顶点j(即vexs[j])"是邻接点;matrix[i][j]=0,则表示它们不是邻接点。
EData是邻接矩阵边对应的结构体。
2. 迪杰斯特拉算法
/* * Dijkstra最短路径。 * 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。 * * 参数说明: * G -- 图 * vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。 * prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。 * dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。 */void dijkstra(Graph G, int vs, int prev[], int dist[]){ int i,j,k; int min; int tmp; int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。 // 初始化 for (i = 0; i < G.vexnum; i++) { flag[i] = 0; // 顶点i的最短路径还没获取到。 prev[i] = 0; // 顶点i的前驱顶点为0。 dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。 } // 对"顶点vs"自身进行初始化 flag[vs] = 1; dist[vs] = 0; // 遍历G.vexnum-1次;每次找出一个顶点的最短路径。 for (i = 1; i < G.vexnum; i++) { // 寻找当前最小的路径; // 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。 min = INF; for (j = 0; j < G.vexnum; j++) { if (flag[j]==0 && dist[j]<min) { min = dist[j]; k = j; } } // 标记"顶点k"为已经获取到最短路径 flag[k] = 1; // 修正当前最短路径和前驱顶点 // 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。 for (j = 0; j < G.vexnum; j++) { tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出 if (flag[j] == 0 && (tmp < dist[j]) ) { dist[j] = tmp; prev[j] = k; } } } // 打印dijkstra最短路径的结果 printf("dijkstra(%c): \n", G.vexs[vs]); for (i = 0; i < G.vexnum; i++) printf(" shortest(%c, %c)=%d\n", G.vexs[vs], G.vexs[i], dist[i]);}
迪杰斯特拉算法的源码
这里分别给出"邻接矩阵图"和"邻接表图"的迪杰斯特拉算法源码。
1. 邻接矩阵源码(matrix_udg.c)
2. 邻接表源码(list_udg.c)
/*** C: Dijkstra算法获取最短路径(邻接矩阵)** @author skywang* @date 2014/04/24*/#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include <string.h>#define MAX 100 // 矩阵最大容量#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))#define LENGTH(a) (sizeof(a)/sizeof(a[0]))// 邻接矩阵typedef struct _graph{char vexs[MAX]; // 顶点集合int vexnum; // 顶点数int edgnum; // 边数int matrix[MAX][MAX]; // 邻接矩阵}Graph, *PGraph;// 边的结构体typedef struct _EdgeData{char start; // 边的起点char end; // 边的终点int weight; // 边的权重}EData;/** 返回ch在matrix矩阵中的位置*/static int get_position(Graph G, char ch){int i;for(i=0; i<G.vexnum; i++)if(G.vexs[i]==ch)return i;return -1;}/** 读取一个输入字符*/static char read_char(){char ch;do {ch = getchar();} while(!isLetter(ch));return ch;}/** 创建图(自己输入)*/Graph* create_graph(){char c1, c2;int v, e;int i, j, weight, p1, p2;Graph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i] = read_char();}// 1. 初始化"边"的权值for (i = 0; i < pG->vexnum; i++){for (j = 0; j < pG->vexnum; j++){if (i==j)pG->matrix[i][j] = 0;elsepG->matrix[i][j] = INF;}}// 2. 初始化"边"的权值: 根据用户的输入进行初始化for (i = 0; i < pG->edgnum; i++){// 读取边的起始顶点,结束顶点,权值printf("edge(%d):", i);c1 = read_char();c2 = read_char();scanf("%d", &weight);p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);if (p1==-1 || p2==-1){printf("input error: invalid edge!\n");free(pG);return NULL;}pG->matrix[p1][p2] = weight;pG->matrix[p2][p1] = weight;}return pG;}/** 创建图(用已提供的矩阵)*/Graph* create_example_graph(){char vexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};int matrix[][9] = {/*A*//*B*//*C*//*D*//*E*//*F*//*G*//*A*/ { 0, 12, INF, INF, INF, 16, 14},/*B*/ { 12, 0, 10, INF, INF, 7, INF},/*C*/ { INF, 10, 0, 3, 5, 6, INF},/*D*/ { INF, INF, 3, 0, 4, INF, INF},/*E*/ { INF, INF, 5, 4, 0, 2, 8},/*F*/ { 16, 7, 6, INF, 2, 0, 9},/*G*/ { 14, INF, INF, INF, 8, 9, 0}};int vlen = LENGTH(vexs);int i, j;Graph* pG;// 输入"顶点数"和"边数"if ((pG=(Graph*)malloc(sizeof(Graph))) == NULL )return NULL;memset(pG, 0, sizeof(Graph));// 初始化"顶点数"pG->vexnum = vlen;// 初始化"顶点"for (i = 0; i < pG->vexnum; i++)pG->vexs[i] = vexs[i];// 初始化"边"for (i = 0; i < pG->vexnum; i++)for (j = 0; j < pG->vexnum; j++)pG->matrix[i][j] = matrix[i][j];// 统计边的数目for (i = 0; i < pG->vexnum; i++)for (j = 0; j < pG->vexnum; j++)if (i!=j && pG->matrix[i][j]!=INF)pG->edgnum++;pG->edgnum /= 2;return pG;}/** 返回顶点v的第一个邻接顶点的索引,失败则返回-1*/static int first_vertex(Graph G, int v){int i;if (v<0 || v>(G.vexnum-1))return -1;for (i = 0; i < G.vexnum; i++)if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)return i;return -1;}/** 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1*/static int next_vertix(Graph G, int v, int w){int i;if (v<0 || v>(G.vexnum-1) || w<0 || w>(G.vexnum-1))return -1;for (i = w + 1; i < G.vexnum; i++)if (G.matrix[v][i]!=0 && G.matrix[v][i]!=INF)return i;return -1;}/** 深度优先搜索遍历图的递归实现*/static void DFS(Graph G, int i, int *visited){int w;visited[i] = 1;printf("%c ", G.vexs[i]);// 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走for (w = first_vertex(G, i); w >= 0; w = next_vertix(G, i, w)){if (!visited[w])DFS(G, w, visited);}}/** 深度优先搜索遍历图*/void DFSTraverse(Graph G){int i;int visited[MAX]; // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){//printf("\n== LOOP(%d)\n", i);if (!visited[i])DFS(G, i, visited);}printf("\n");}/** 广度优先搜索(类似于树的层次遍历)*/void BFS(Graph G){int head = 0;int rear = 0;int queue[MAX]; // 辅组队列int visited[MAX]; // 顶点访问标记int i, j, k;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i]);queue[rear++] = i; // 入队列}while (head != rear){j = queue[head++]; // 出队列for (k = first_vertex(G, j); k >= 0; k = next_vertix(G, j, k)) //k是为访问的邻接顶点{if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k]);queue[rear++] = k;}}}}printf("\n");}/** 打印矩阵队列图*/void print_graph(Graph G){int i,j;printf("Martix Graph:\n");for (i = 0; i < G.vexnum; i++){for (j = 0; j < G.vexnum; j++)printf("%10d ", G.matrix[i][j]);printf("\n");}}/** prim最小生成树** 参数说明:* G -- 邻接矩阵图* start -- 从图中的第start个元素开始,生成最小树*/void prim(Graph G, int start){int min,i,j,k,m,n,sum;int index=0; // prim最小树的索引,即prims数组的索引char prims[MAX]; // prim最小树的结果数组int weights[MAX]; // 顶点间边的权值// prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。prims[index++] = G.vexs[start];// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。for (i = 0; i < G.vexnum; i++ )weights[i] = G.matrix[start][i];// 将第start个顶点的权值初始化为0。// 可以理解为"第start个顶点到它自身的距离为0"。weights[start] = 0;for (i = 0; i < G.vexnum; i++){// 由于从start开始的,因此不需要再对第start个顶点进行处理。if(start == i)continue;j = 0;k = 0;min = INF;// 在未被加入到最小生成树的顶点中,找出权值最小的顶点。while (j < G.vexnum){// 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。if (weights[j] != 0 && weights[j] < min){min = weights[j];k = j;}j++;}// 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。// 将第k个顶点加入到最小生成树的结果数组中prims[index++] = G.vexs[k];// 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。weights[k] = 0;// 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。for (j = 0 ; j < G.vexnum; j++){// 当第j个节点没有被处理,并且需要更新时才被更新。if (weights[j] != 0 && G.matrix[k][j] < weights[j])weights[j] = G.matrix[k][j];}}// 计算最小生成树的权值sum = 0;for (i = 1; i < index; i++){min = INF;// 获取prims[i]在G中的位置n = get_position(G, prims[i]);// 在vexs[0...i]中,找出到j的权值最小的顶点。for (j = 0; j < i; j++){m = get_position(G, prims[j]);if (G.matrix[m][n]<min)min = G.matrix[m][n];}sum += min;}// 打印最小生成树printf("PRIM(%c)=%d: ", G.vexs[start], sum);for (i = 0; i < index; i++)printf("%c ", prims[i]);printf("\n");}/** 获取图中的边*/EData* get_edges(Graph G){int i,j;int index=0;EData *edges;edges = (EData*)malloc(G.edgnum*sizeof(EData));for (i=0;i < G.vexnum;i++){for (j=i+1;j < G.vexnum;j++){if (G.matrix[i][j]!=INF){edges[index].start = G.vexs[i];edges[index].end = G.vexs[j];edges[index].weight = G.matrix[i][j];index++;}}}return edges;}/** 对边按照权值大小进行排序(由小到大)*/void sorted_edges(EData* edges, int elen){int i,j;for (i=0; i<elen; i++){for (j=i+1; j<elen; j++){if (edges[i].weight > edges[j].weight){// 交换"第i条边"和"第j条边"EData tmp = edges[i];edges[i] = edges[j];edges[j] = tmp;}}}}/** 获取i的终点*/int get_end(int vends[], int i){while (vends[i] != 0)i = vends[i];return i;}/** 克鲁斯卡尔(Kruskal)最小生成树*/void kruskal(Graph G){int i,m,n,p1,p2;int length;int index = 0; // rets数组的索引int vends[MAX]={0}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边EData *edges; // 图对应的所有边// 获取"图中所有的边"edges = get_edges(G);// 将边按照"权"的大小进行排序(从小到大)sorted_edges(edges, G.edgnum);for (i=0; i<G.edgnum; i++){p1 = get_position(G, edges[i].start); // 获取第i条边的"起点"的序号p2 = get_position(G, edges[i].end); // 获取第i条边的"终点"的序号m = get_end(vends, p1); // 获取p1在"已有的最小生成树"中的终点n = get_end(vends, p2); // 获取p2在"已有的最小生成树"中的终点// 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路if (m != n){vends[m] = n; // 设置m在"已有的最小生成树"中的终点为nrets[index++] = edges[i]; // 保存结果}}free(edges);// 统计并打印"kruskal最小生成树"的信息length = 0;for (i = 0; i < index; i++)length += rets[i].weight;printf("Kruskal=%d: ", length);for (i = 0; i < index; i++)printf("(%c,%c) ", rets[i].start, rets[i].end);printf("\n");}/** Dijkstra最短路径。* 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。** 参数说明:* G -- 图* vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。* prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。* dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。*/void dijkstra(Graph G, int vs, int prev[], int dist[]){int i,j,k;int min;int tmp;int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。// 初始化for (i = 0; i < G.vexnum; i++){flag[i] = 0; // 顶点i的最短路径还没获取到。prev[i] = 0; // 顶点i的前驱顶点为0。dist[i] = G.matrix[vs][i];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。}// 对"顶点vs"自身进行初始化flag[vs] = 1;dist[vs] = 0;// 遍历G.vexnum-1次;每次找出一个顶点的最短路径。for (i = 1; i < G.vexnum; i++){// 寻找当前最小的路径;// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。min = INF;for (j = 0; j < G.vexnum; j++){if (flag[j]==0 && dist[j]<min){min = dist[j];k = j;}}// 标记"顶点k"为已经获取到最短路径flag[k] = 1;// 修正当前最短路径和前驱顶点// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。for (j = 0; j < G.vexnum; j++){tmp = (G.matrix[k][j]==INF ? INF : (min + G.matrix[k][j])); // 防止溢出if (flag[j] == 0 && (tmp < dist[j]) ){dist[j] = tmp;prev[j] = k;}}}// 打印dijkstra最短路径的结果printf("dijkstra(%c): \n", G.vexs[vs]);for (i = 0; i < G.vexnum; i++)printf(" shortest(%c, %c)=%d\n", G.vexs[vs], G.vexs[i], dist[i]);}void main(){int prev[MAX] = {0};int dist[MAX] = {0};Graph* pG;// 自定义"图"(输入矩阵队列)//pG = create_graph();// 采用已有的"图"pG = create_example_graph();//print_graph(*pG); // 打印图//DFSTraverse(*pG); // 深度优先遍历//BFS(*pG); // 广度优先遍历//prim(*pG, 0); // prim算法生成最小生成树//kruskal(*pG); // kruskal算法生成最小生成树// dijkstra算法获取"第4个顶点"到其它各个顶点的最短距离dijkstra(*pG, 3, prev, dist);}
/*** C: Dijkstra算法获取最短路径(邻接表)** @author skywang* @date 2014/04/24*/#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include <string.h>#define MAX 100#define INF (~(0x1<<31)) // 最大值(即0X7FFFFFFF)#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))#define LENGTH(a) (sizeof(a)/sizeof(a[0]))// 邻接表中表对应的链表的顶点typedef struct _ENode{int ivex; // 该边的顶点的位置int weight; // 该边的权struct _ENode *next_edge; // 指向下一条弧的指针}ENode, *PENode;// 邻接表中表的顶点typedef struct _VNode{char data; // 顶点信息ENode *first_edge; // 指向第一条依附该顶点的弧}VNode;// 邻接表typedef struct _LGraph{int vexnum; // 图的顶点的数目int edgnum; // 图的边的数目VNode vexs[MAX];}LGraph;/** 返回ch在matrix矩阵中的位置*/static int get_position(LGraph G, char ch){int i;for(i=0; i<G.vexnum; i++)if(G.vexs[i].data==ch)return i;return -1;}/** 读取一个输入字符*/static char read_char(){char ch;do {ch = getchar();} while(!isLetter(ch));return ch;}/** 将node链接到list的末尾*/static void link_last(ENode *list, ENode *node){ENode *p = list;while(p->next_edge)p = p->next_edge;p->next_edge = node;}/** 创建邻接表对应的图(自己输入)*/LGraph* create_lgraph(){char c1, c2;int v, e;int i, p1, p2;int weight;ENode *node1, *node2;LGraph* pG;// 输入"顶点数"和"边数"printf("input vertex number: ");scanf("%d", &v);printf("input edge number: ");scanf("%d", &e);if ( v < 1 || e < 1 || (e > (v * (v-1)))){printf("input error: invalid parameters!\n");return NULL;}if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = v;pG->edgnum = e;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){printf("vertex(%d): ", i);pG->vexs[i].data = read_char();pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点,结束顶点,权printf("edge(%d): ", i);c1 = read_char();c2 = read_char();scanf("%d", &weight);p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex = p2;node1->weight = weight;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)malloc(sizeof(ENode));node2->ivex = p1;node2->weight = weight;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;}// 边的结构体typedef struct _edata{char start; // 边的起点char end; // 边的终点int weight; // 边的权重}EData;// 顶点static char gVexs[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};// 边static EData gEdges[] = {// 起点 终点 权{'A', 'B', 12},{'A', 'F', 16},{'A', 'G', 14},{'B', 'C', 10},{'B', 'F', 7},{'C', 'D', 3},{'C', 'E', 5},{'C', 'F', 6},{'D', 'E', 4},{'E', 'F', 2},{'E', 'G', 8},{'F', 'G', 9},};/** 创建邻接表对应的图(用已提供的数据)*/LGraph* create_example_lgraph(){char c1, c2;int vlen = LENGTH(gVexs);int elen = LENGTH(gEdges);int i, p1, p2;int weight;ENode *node1, *node2;LGraph* pG;if ((pG=(LGraph*)malloc(sizeof(LGraph))) == NULL )return NULL;memset(pG, 0, sizeof(LGraph));// 初始化"顶点数"和"边数"pG->vexnum = vlen;pG->edgnum = elen;// 初始化"邻接表"的顶点for(i=0; i<pG->vexnum; i++){pG->vexs[i].data = gVexs[i];pG->vexs[i].first_edge = NULL;}// 初始化"邻接表"的边for(i=0; i<pG->edgnum; i++){// 读取边的起始顶点,结束顶点,权c1 = gEdges[i].start;c2 = gEdges[i].end;weight = gEdges[i].weight;p1 = get_position(*pG, c1);p2 = get_position(*pG, c2);// 初始化node1node1 = (ENode*)malloc(sizeof(ENode));node1->ivex = p2;node1->weight = weight;// 将node1链接到"p1所在链表的末尾"if(pG->vexs[p1].first_edge == NULL)pG->vexs[p1].first_edge = node1;elselink_last(pG->vexs[p1].first_edge, node1);// 初始化node2node2 = (ENode*)malloc(sizeof(ENode));node2->ivex = p1;node2->weight = weight;// 将node2链接到"p2所在链表的末尾"if(pG->vexs[p2].first_edge == NULL)pG->vexs[p2].first_edge = node2;elselink_last(pG->vexs[p2].first_edge, node2);}return pG;}/** 深度优先搜索遍历图的递归实现*/static void DFS(LGraph G, int i, int *visited){int w;ENode *node;visited[i] = 1;printf("%c ", G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){if (!visited[node->ivex])DFS(G, node->ivex, visited);node = node->next_edge;}}/** 深度优先搜索遍历图*/void DFSTraverse(LGraph G){int i;int visited[MAX]; // 顶点访问标记// 初始化所有顶点都没有被访问for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("DFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i])DFS(G, i, visited);}printf("\n");}/** 广度优先搜索(类似于树的层次遍历)*/void BFS(LGraph G){int head = 0;int rear = 0;int queue[MAX]; // 辅组队列int visited[MAX]; // 顶点访问标记int i, j, k;ENode *node;for (i = 0; i < G.vexnum; i++)visited[i] = 0;printf("BFS: ");for (i = 0; i < G.vexnum; i++){if (!visited[i]){visited[i] = 1;printf("%c ", G.vexs[i].data);queue[rear++] = i; // 入队列}while (head != rear){j = queue[head++]; // 出队列node = G.vexs[j].first_edge;while (node != NULL){k = node->ivex;if (!visited[k]){visited[k] = 1;printf("%c ", G.vexs[k].data);queue[rear++] = k;}node = node->next_edge;}}}printf("\n");}/** 打印邻接表图*/void print_lgraph(LGraph G){int i,j;ENode *node;printf("List Graph:\n");for (i = 0; i < G.vexnum; i++){printf("%d(%c): ", i, G.vexs[i].data);node = G.vexs[i].first_edge;while (node != NULL){printf("%d(%c) ", node->ivex, G.vexs[node->ivex].data);node = node->next_edge;}printf("\n");}}/** 获取G中边<start, end>的权值;若start和end不是连通的,则返回无穷大。*/int get_weight(LGraph G, int start, int end){ENode *node;if (start==end)return 0;node = G.vexs[start].first_edge;while (node!=NULL){if (end==node->ivex)return node->weight;node = node->next_edge;}return INF;}/** prim最小生成树** 参数说明:* G -- 邻接表图* start -- 从图中的第start个元素开始,生成最小树*/void prim(LGraph G, int start){int min,i,j,k,m,n,tmp,sum;int index=0; // prim最小树的索引,即prims数组的索引char prims[MAX]; // prim最小树的结果数组int weights[MAX]; // 顶点间边的权值// prim最小生成树中第一个数是"图中第start个顶点",因为是从start开始的。prims[index++] = G.vexs[start].data;// 初始化"顶点的权值数组",// 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。for (i = 0; i < G.vexnum; i++ )weights[i] = get_weight(G, start, i);for (i = 0; i < G.vexnum; i++){// 由于从start开始的,因此不需要再对第start个顶点进行处理。if(start == i)continue;j = 0;k = 0;min = INF;// 在未被加入到最小生成树的顶点中,找出权值最小的顶点。while (j < G.vexnum){// 若weights[j]=0,意味着"第j个节点已经被排序过"(或者说已经加入了最小生成树中)。if (weights[j] != 0 && weights[j] < min){min = weights[j];k = j;}j++;}// 经过上面的处理后,在未被加入到最小生成树的顶点中,权值最小的顶点是第k个顶点。// 将第k个顶点加入到最小生成树的结果数组中prims[index++] = G.vexs[k].data;// 将"第k个顶点的权值"标记为0,意味着第k个顶点已经排序过了(或者说已经加入了最小树结果中)。weights[k] = 0;// 当第k个顶点被加入到最小生成树的结果数组中之后,更新其它顶点的权值。for (j = 0 ; j < G.vexnum; j++){// 获取第k个顶点到第j个顶点的权值tmp = get_weight(G, k, j);// 当第j个节点没有被处理,并且需要更新时才被更新。if (weights[j] != 0 && tmp < weights[j])weights[j] = tmp;}}// 计算最小生成树的权值sum = 0;for (i = 1; i < index; i++){min = INF;// 获取prims[i]在G中的位置n = get_position(G, prims[i]);// 在vexs[0...i]中,找出到j的权值最小的顶点。for (j = 0; j < i; j++){m = get_position(G, prims[j]);tmp = get_weight(G, m, n);if (tmp < min)min = tmp;}sum += min;}// 打印最小生成树printf("PRIM(%c)=%d: ", G.vexs[start].data, sum);for (i = 0; i < index; i++)printf("%c ", prims[i]);printf("\n");}/** 获取图中的边*/EData* get_edges(LGraph G){int i,j;int index=0;ENode *node;EData *edges;edges = (EData*)malloc(G.edgnum*sizeof(EData));for (i=0; i<G.vexnum; i++){node = G.vexs[i].first_edge;while (node != NULL){if (node->ivex > i){edges[index].start = G.vexs[i].data; // 起点edges[index].end = G.vexs[node->ivex].data; // 终点edges[index].weight = node->weight; // 权index++;}node = node->next_edge;}}return edges;}/** 对边按照权值大小进行排序(由小到大)*/void sorted_edges(EData* edges, int elen){int i,j;for (i=0; i<elen; i++){for (j=i+1; j<elen; j++){if (edges[i].weight > edges[j].weight){// 交换"第i条边"和"第j条边"EData tmp = edges[i];edges[i] = edges[j];edges[j] = tmp;}}}}/** 获取i的终点*/int get_end(int vends[], int i){while (vends[i] != 0)i = vends[i];return i;}/** 克鲁斯卡尔(Kruskal)最小生成树*/void kruskal(LGraph G){int i,m,n,p1,p2;int length;int index = 0; // rets数组的索引int vends[MAX]={0}; // 用于保存"已有最小生成树"中每个顶点在该最小树中的终点。EData rets[MAX]; // 结果数组,保存kruskal最小生成树的边EData *edges; // 图对应的所有边// 获取"图中所有的边"edges = get_edges(G);// 将边按照"权"的大小进行排序(从小到大)sorted_edges(edges, G.edgnum);for (i=0; i<G.edgnum; i++){p1 = get_position(G, edges[i].start); // 获取第i条边的"起点"的序号p2 = get_position(G, edges[i].end); // 获取第i条边的"终点"的序号m = get_end(vends, p1); // 获取p1在"已有的最小生成树"中的终点n = get_end(vends, p2); // 获取p2在"已有的最小生成树"中的终点// 如果m!=n,意味着"边i"与"已经添加到最小生成树中的顶点"没有形成环路if (m != n){vends[m] = n; // 设置m在"已有的最小生成树"中的终点为nrets[index++] = edges[i]; // 保存结果}}free(edges);// 统计并打印"kruskal最小生成树"的信息length = 0;for (i = 0; i < index; i++)length += rets[i].weight;printf("Kruskal=%d: ", length);for (i = 0; i < index; i++)printf("(%c,%c) ", rets[i].start, rets[i].end);printf("\n");}/** Dijkstra最短路径。* 即,统计图(G)中"顶点vs"到其它各个顶点的最短路径。** 参数说明:* G -- 图* vs -- 起始顶点(start vertex)。即计算"顶点vs"到其它顶点的最短路径。* prev -- 前驱顶点数组。即,prev[i]的值是"顶点vs"到"顶点i"的最短路径所经历的全部顶点中,位于"顶点i"之前的那个顶点。* dist -- 长度数组。即,dist[i]是"顶点vs"到"顶点i"的最短路径的长度。*/void dijkstra(LGraph G, int vs, int prev[], int dist[]){int i,j,k;int min;int tmp;int flag[MAX]; // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。// 初始化for (i = 0; i < G.vexnum; i++){flag[i] = 0; // 顶点i的最短路径还没获取到。prev[i] = 0; // 顶点i的前驱顶点为0。dist[i] = get_weight(G, vs, i); // 顶点i的最短路径为"顶点vs"到"顶点i"的权。}// 对"顶点vs"自身进行初始化flag[vs] = 1;dist[vs] = 0;// 遍历G.vexnum-1次;每次找出一个顶点的最短路径。for (i = 1; i < G.vexnum; i++){// 寻找当前最小的路径;// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。min = INF;for (j = 0; j < G.vexnum; j++){if (flag[j]==0 && dist[j]<min){min = dist[j];k = j;}}// 标记"顶点k"为已经获取到最短路径flag[k] = 1;// 修正当前最短路径和前驱顶点// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。for (j = 0; j < G.vexnum; j++){tmp = get_weight(G, k, j);tmp = (tmp==INF ? INF : (min + tmp)); // 防止溢出if (flag[j] == 0 && (tmp < dist[j]) ){dist[j] = tmp;prev[j] = k;}}}// 打印dijkstra最短路径的结果printf("dijkstra(%c): \n", G.vexs[vs].data);for (i = 0; i < G.vexnum; i++)printf(" shortest(%c, %c)=%d\n", G.vexs[vs].data, G.vexs[i].data, dist[i]);}void main(){int prev[MAX] = {0};int dist[MAX] = {0};LGraph* pG;// 自定义"图"(自己输入数据)//pG = create_lgraph();// 采用已有的"图"pG = create_example_lgraph();//print_lgraph(*pG); // 打印图//DFSTraverse(*pG); // 深度优先遍历//BFS(*pG); // 广度优先遍历//prim(*pG, 0); // prim算法生成最小生成树//kruskal(*pG); // kruskal算法生成最小生成树// dijkstra算法获取"第4个顶点"到其它各个顶点的最短距离dijkstra(*pG, 3, prev, dist);}
- Dijkstra算法(一)之 C语言详解
- Dijkstra算法 之 C语言详解
- Kruskal算法(一)之 C语言详解
- Prim算法(一)之 C语言详解
- dijkstra算法 c语言
- Dijkstra改进算法C语言
- c语言 dijkstra算法 交通图
- Dijkstra算法 c语言实现
- dijkstra算法C语言实现
- 最短路径之dijkstra算法的C语言实现
- 最短路径之Dijkstra算法 C语言实现
- Dijkstra算法之 Java详解
- Dijkstra算法的C语言程序
- Dijkstra算法的C语言程序
- dijkstra算法(C语言 邻接表)
- Dijkstra 算法 c语言原生实现
- 哈夫曼树(一)之 C语言详解
- 拓扑排序(一)之 C语言详解
- 图的遍历之 深度优先搜索和广度优先搜索
- 拓扑排序(一)之 C语言详解
- Google服务器 IP
- Kruskal算法(一)之 C语言详解
- Prim算法(一)之 C语言详解
- Dijkstra算法(一)之 C语言详解
- 冒泡排序
- 快速排序
- 直接插入排序
- 希尔排序
- 选择排序
- 堆排序
- 归并排序
- 桶排序