当前位置: 首页 > news >正文

C语言-数据结构 无向图迪杰斯特拉算法(Dijkstra)邻接矩阵存储

        在迪杰斯特拉中,相比普利姆算法,是从顶点出发的一条路径不断的寻找最短路径,在实现的时候需要创建三个辅助数组,记录算法的关键操作,分别是Visited[MAXVEX]记录顶点是否被访问,教材上写的final数组但作用是一样的,然后第二个数组是TmpDistance[MAXVEX],教材使用的D数组,命名语义化较弱不太好理解,实际用途与TmpDistance一样的,用于记录算法过程中,当前顶点到达邻接顶点的最小值,Path[MAXVEX]用于记录每个顶点的前驱结点,通过它可以记录到达所有顶点路径的路线,非常巧妙!整个算法过程非常像一只长了触角的贪吃蛇,用最短的身长到达所有顶点,触手想象成每次循环中探索最短邻接顶点的操作。

 我们将创建下面的无向权值图:

84ef03cba4ba478383b338ae5884012a.png

  邻接矩阵的绘制还是手动赋值上三角,并通过矩阵对称性生成整个邻接矩阵,其中最小生成树中需要用到权值,对应原本有边的地方之前我是用1表示,现在改成边对应的权值,之前的0表示没有边,现在改成99表示为无穷,其实应该换成更大的值以确保树的边权值都小于这个最大值,但为了方便对齐显示看邻接矩阵,就使用了比本图中各边长较大的99来表示最大值。

9eefaa5c866742cbb239f5f9de2aff7d.png

        Dijkstra算法代码

//v0起点,path路径,d临时辅助最短路径数组
void Dijkstra(MGraph G, int Vstart,int TmpDistance[],int Path[]) {int v, w, UpcomingVex, min;int Visited[MAXVEX];//初始化所有顶点for (v = 0; v < G.numNodes; v++) {Visited[v] = FALSE;TmpDistance[v] = G.arc[Vstart][v];Path[v] = -1;}//起始点路径长度为0TmpDistance[Vstart] = 0;//起始点标记为已经被访问Visited[Vstart] = TRUE;//遍历所有顶点,总计遍历n-1次所以,主循环控制次数就从1开始,求得当前顶点Vi到达相邻结点的最近距离的顶点,并标记Visited为已经找到for (v = 1; v < G.numNodes; v++) {min = GRAPH_INFINITY; // 初始化最小距离为无穷大// 寻找当前未访问的顶点中距离起始点最近的顶点for (w = 0; w < G.numNodes; w++) {if (!Visited[w] && TmpDistance[w] < min) {UpcomingVex = w;min = TmpDistance[w];}}// 标记找到的顶点为已访问Visited[UpcomingVex] = true;// 更新从当前顶点到其他顶点的最短路径for (w = 0; w < G.numNodes; w++) {if (!Visited[w] && (min + G.arc[UpcomingVex][w] < TmpDistance[w])) {TmpDistance[w] = min + G.arc[UpcomingVex][w];Path[w] = UpcomingVex; // 记录路径}}}
}

打印路径函数,根据Dijkstra算法计算的Path数组进行,Path数组用到了并查集的部分思想,Dijkstra中我们所有顶点都是一个集合,-1表示前驱结点为自身即找到起点,也表示为同一个集合,搜索路径的过程需要一步一步往回查找,用到了并查集中的Find函数,比如A->B->C,我们Path数组是从C顶点出发然后根据path存储的内容找到前驱结点B然后再找到A,我这里为了方便直接用数组存储路径,逆序打印就行,也可以用栈进行存储。

// 打印从起点到各顶点的路径
void PathVexPrint(int Path[]) {for (int start = 0; start < MAXVEX; start++) {int i = start;char tmp[10] = "";int count = 0;// 追踪路径while (i >= 0) {tmp[count++] = 'A' + i; // 假设顶点用字母表示i = Path[i];}tmp[count++] = 'A'; // 添加起始点// 反向输出路径for (int j = count - 1; j >= 0; j--) {printf("%c", tmp[j]);if (j > 0) {printf("->");}}printf("\n");}
}

完整代码(包含邻接矩阵的创建、迪杰斯特拉算法)

#include "stdio.h"    
#include "stdlib.h"   
#include "math.h"  
#include "time.h"// 禁用特定的警告
#pragma warning(disable:4996)// 定义一些常量和数据类型
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 8 /* 最大顶点数,用户定义 */
#define MAXEDGE 10 /* 最大边数,用户定义 */
#define GRAPH_INFINITY 99 /* 用0表示∞,表示不存在边 *//* 定义状态、顶点和边的类型 */
typedef int Status;  /* Status是函数的返回类型,如OK表示成功 */
typedef char VertexType; /* 顶点的类型,用字符表示 */
typedef int EdgeType; /* 边上的权值类型,用整数表示 */
typedef int Boolean; /* 布尔类型 */
// 定义顶点标签
char Array[] = "ABCDEFGHI";/* 图的邻接矩阵结构体 */
typedef struct
{VertexType vexs[MAXVEX]; /* 顶点表 */EdgeType arc[MAXVEX][MAXVEX]; /* 邻接矩阵,表示边的权值 */int numNodes, numEdges; /* 图中当前的顶点数和边数 */
} MGraph;/* 创建一个无向网图的邻接矩阵表示 */
void CreateMGraph(MGraph* G)
{int i, j, k, w;// 初始化图的顶点数和边数G->numNodes = 8;G->numEdges = 10;// 初始化邻接矩阵和顶点表for (i = 0; i < G->numNodes; i++) {for (j = 0; j < G->numNodes; j++) {G->arc[i][j] = GRAPH_INFINITY; /* 初始化邻接矩阵为∞ */}G->vexs[i] = Array[i]; /* 初始化顶点表 */}G->arc[0][0] = GRAPH_INFINITY;G->arc[0][1] = 10;G->arc[0][2] = GRAPH_INFINITY;G->arc[0][3] = GRAPH_INFINITY;G->arc[0][4] = GRAPH_INFINITY;G->arc[0][5] = 11;G->arc[0][6] = GRAPH_INFINITY;G->arc[0][7] = GRAPH_INFINITY;G->arc[1][0] = GRAPH_INFINITY;G->arc[1][1] = GRAPH_INFINITY;G->arc[1][2] = 23;G->arc[1][3] = GRAPH_INFINITY;G->arc[1][4] = GRAPH_INFINITY;G->arc[1][5] = GRAPH_INFINITY;G->arc[1][6] = 12;G->arc[1][7] = GRAPH_INFINITY;G->arc[2][0] = GRAPH_INFINITY;G->arc[2][1] = GRAPH_INFINITY;G->arc[2][2] = GRAPH_INFINITY;G->arc[2][3] = 21;G->arc[2][4] = GRAPH_INFINITY;G->arc[2][5] = GRAPH_INFINITY;G->arc[2][6] = GRAPH_INFINITY;G->arc[2][7] = GRAPH_INFINITY;G->arc[3][0] = GRAPH_INFINITY;G->arc[3][1] = GRAPH_INFINITY;G->arc[3][2] = GRAPH_INFINITY;G->arc[3][3] = GRAPH_INFINITY;G->arc[3][4] = GRAPH_INFINITY;G->arc[3][5] = GRAPH_INFINITY;G->arc[3][6] = GRAPH_INFINITY;G->arc[3][7] = 11;G->arc[4][0] = GRAPH_INFINITY;G->arc[4][1] = GRAPH_INFINITY;G->arc[4][2] = GRAPH_INFINITY;G->arc[4][3] = GRAPH_INFINITY;G->arc[4][4] = GRAPH_INFINITY;G->arc[4][5] = 47;G->arc[4][6] = GRAPH_INFINITY;G->arc[4][7] = 80;G->arc[5][0] = GRAPH_INFINITY;G->arc[5][1] = GRAPH_INFINITY;G->arc[5][2] = GRAPH_INFINITY;G->arc[5][3] = GRAPH_INFINITY;G->arc[5][4] = GRAPH_INFINITY;G->arc[5][5] = GRAPH_INFINITY;G->arc[5][6] = 6;G->arc[5][7] = GRAPH_INFINITY;G->arc[6][0] = GRAPH_INFINITY;G->arc[6][1] = GRAPH_INFINITY;G->arc[6][2] = GRAPH_INFINITY;G->arc[6][3] = GRAPH_INFINITY;G->arc[6][4] = GRAPH_INFINITY;G->arc[6][5] = GRAPH_INFINITY;G->arc[6][6] = GRAPH_INFINITY;G->arc[6][7] = 8;G->arc[7][0] = GRAPH_INFINITY;G->arc[7][1] = GRAPH_INFINITY;G->arc[7][2] = GRAPH_INFINITY;G->arc[7][3] = GRAPH_INFINITY;G->arc[7][4] = GRAPH_INFINITY;G->arc[7][5] = GRAPH_INFINITY;G->arc[7][6] = GRAPH_INFINITY;G->arc[7][7] = GRAPH_INFINITY;// 由于是无向图,邻接矩阵是对称的,需要将其对称for (int i = 0; i < G->numNodes; i++) {for (int j = 0; j < G->numNodes; j++) {G->arc[j][i] = G->arc[i][j];}}// 打印邻接矩阵printf("邻接矩阵为:\n");printf("     ");for (int i = 0; i < G->numNodes; i++) {printf("%2d ", i); /* 打印列索引 */}printf("\n     ");for (int i = 0; i < G->numNodes; i++) {printf("%2c ", G->vexs[i]); /* 打印顶点标签 */}printf("\n");for (int i = 0; i < G->numNodes; i++) {printf("%2d", i); /* 打印行索引 */printf("%2c ", G->vexs[i]); /* 打印顶点标签 */for (int j = 0; j < G->numNodes; j++) {if (G->arc[i][j] != 99) {printf("\033[31m%02d \033[0m", G->arc[i][j]); /* 打印邻接矩阵中的权值 */}else {printf("%02d ", G->arc[i][j]); /* 打印邻接矩阵中的权值 */}}printf("\n");}
}//v0起点,path路径,d临时辅助最短路径数组
void Dijkstra(MGraph G, int Vstart,int TmpDistance[],int Path[]) {int v, w, UpcomingVex, min;int Visited[MAXVEX];//初始化所有顶点for (v = 0; v < G.numNodes; v++) {Visited[v] = FALSE;TmpDistance[v] = G.arc[Vstart][v];Path[v] = -1;}//起始点路径长度为0TmpDistance[Vstart] = 0;//起始点标记为已经被访问Visited[Vstart] = TRUE;//遍历所有顶点,总计遍历n-1次所以,主循环控制次数就从1开始,求得当前顶点Vi到达相邻结点的最近距离的顶点,并标记Visited为已经找到for (v = 1; v < G.numNodes; v++) {min = GRAPH_INFINITY; // 初始化最小距离为无穷大// 寻找当前未访问的顶点中距离起始点最近的顶点for (w = 0; w < G.numNodes; w++) {if (!Visited[w] && TmpDistance[w] < min) {UpcomingVex = w;min = TmpDistance[w];}}// 标记找到的顶点为已访问Visited[UpcomingVex] = true;// 更新从当前顶点到其他顶点的最短路径for (w = 0; w < G.numNodes; w++) {if (!Visited[w] && (min + G.arc[UpcomingVex][w] < TmpDistance[w])) {TmpDistance[w] = min + G.arc[UpcomingVex][w];Path[w] = UpcomingVex; // 记录路径}}}
}
// 打印从起点到各顶点的路径
void PathVexPrint(int Path[]) {for (int start = 0; start < MAXVEX; start++) {int i = start;char tmp[10] = "";int count = 0;// 追踪路径while (i >= 0) {tmp[count++] = 'A' + i; // 假设顶点用字母表示i = Path[i];}tmp[count++] = 'A'; // 添加起始点// 反向输出路径for (int j = count - 1; j >= 0; j--) {printf("%c", tmp[j]);if (j > 0) {printf("->");}}printf("\n");}
}// 打印数组内容
void InfoPrint(int info[]) {printf("\n");for (int i = 0; i < MAXVEX; i++) {printf("%d ", info[i]);}printf("\n");
}
int main(void)
{MGraph G;/* 创建图 */CreateMGraph(&G);int TmpDistance[MAXVEX],Path[MAXVEX];Dijkstra(G, 0, TmpDistance,Path);printf("到达各顶点最短路径的长度(TmpDistance数组)为:\n");printf("A  B  C  D  E  F  G  H  ");InfoPrint(TmpDistance);printf("Path的值为:");InfoPrint(Path);printf("A顶点出发到达各顶点的路径如下:\n");PathVexPrint(Path);return 0;
}

无向图示意图:

84ef03cba4ba478383b338ae5884012a.png

运行结果:

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • vscode 使用git bash,路径分隔符缺少问题
  • 苍穹外卖学习笔记(三)
  • 深度学习驱动下的字符识别:挑战与创新
  • Vue Router 入门指南:基础配置、路由守卫与动态路由
  • 关于武汉芯景科技有限公司的IIC缓冲器芯片XJ4307开发指南(兼容LTC4307)
  • LabVIEW软件,如何检测连接到的设备?
  • 3.记:Android EditText接收扫码枪输入数据丢失问题
  • 828华为云征文|华为云Flexus X实例docker部署MinIO对象存储系统obs
  • 【机器人工具箱Robotics Toolbox开发笔记(一)】Matlab机器人工具箱简介
  • 如何在Word中插入复选框
  • Linux内核 -- CGROUP子系统之内存控制组 mem_cgroup_charge函数
  • idea中配置Translation插件完成翻译功能
  • 覆盖索引是什么意思?
  • 利用深度学习实现验证码识别-4-ResNet18+imagecaptcha
  • 史上最全-经管类国家社科基金立项名单汇总 1991-2024
  • 【译】JS基础算法脚本:字符串结尾
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • Python学习笔记 字符串拼接
  • Python学习之路16-使用API
  • sublime配置文件
  • vue总结
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 百度小程序遇到的问题
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 排序(1):冒泡排序
  • 前端自动化解决方案
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 想写好前端,先练好内功
  • 小程序 setData 学问多
  • 以太坊客户端Geth命令参数详解
  • #if等命令的学习
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #stm32驱动外设模块总结w5500模块
  • ${factoryList }后面有空格不影响
  • (55)MOS管专题--->(10)MOS管的封装
  • (C11) 泛型表达式
  • (c语言)strcpy函数用法
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (Java入门)抽象类,接口,内部类
  • (Matlab)遗传算法优化的BP神经网络实现回归预测
  • (三)elasticsearch 源码之启动流程分析
  • (四)js前端开发中设计模式之工厂方法模式
  • (五)Python 垃圾回收机制
  • (一)使用Mybatis实现在student数据库中插入一个学生信息
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • .a文件和.so文件
  • .NET Core 发展历程和版本迭代
  • .NET Core 网络数据采集 -- 使用AngleSharp做html解析
  • .Net Core 微服务之Consul(二)-集群搭建
  • .NET Framework Client Profile - a Subset of the .NET Framework Redistribution
  • .net wcf memory gates checking failed
  • .NET6 命令行启动及发布单个Exe文件
  • .net与java建立WebService再互相调用