手撕迪杰斯特拉算法(Dijkstra)及代码(附图解)

本文详细介绍了迪杰斯特拉算法,用于解决从起点到终点的最短路径问题,通过步骤解析和代码实现,展示了如何在带权图中找到最小代价路径,包括算法思想、邻接矩阵构建和核心代码演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

迪杰斯特拉(Dijkstra)算法

引言:我们常常纠结一个对路径选择的决策问题,假设我们要从北京到上海,那么如何才能走 花最少的钱,又最节省时间的线路呢?

​ 这时候,我们可以把从 北京 到 上海 间的路线站标记,那么 北京 到 各路线站 都会有相应的金钱和时间花费,我们只需要找出一条从北京到上海所经过的路线站的时间和金钱总值消耗最少的即可。

​ 显而易见,对应到图中,就是一张带权的图,即 一张 网。我们只需要找出 起点 到 终点 权值之和最少的路径即可。即

t a r g e t = M i n ( ∑ b e g i n e n d w e i g h t ) i target=Min{ (\sum_{begin}^{end}weight)_i } target=Min(beginendweight)i

1.算法思想及其步骤

算 法 核 心 \color{SpringGreen}算法核心 :基于已经求得最短路径的基础上,求得更远顶点的最短路径


​ 图结构 graphNet : 带权的图 (网),用邻接矩阵表示

​ 路径前驱数组 path[ ] :存储 从起点 V0 到 终点 Vm 的 当前 最短路径 终点 的前驱 ,即 V0->Vm的前驱->Vm(此步在接下来会充分解析)

​ 最短路径数组 dist[ ] :存储 从起点 V0 到 终点 Vm 的 当前 最短路径长度,即当前的权值之和

​ 集合数组 S[ ] :检查 从起点 V0 到 某顶点 是否已经求得最短路径

​ 集合 V : 图节点总数


Step1将与V0有连线的顶点之间 对应的最短路径长度 path[ ] 初始化为 权值
Step2选择Vm,使dist[ Vm ]=Min{ dist[ Vi ], v i v_i vi∈V-S}
Step3将Vm加入S中
Step4在求得 dist[ Vm ]的基础上,修正从起点 V0 到集合V-S 上任意顶点 Vi 的最短路径的长度

重复以上 2~4 步共 n-1 次即可求得 V0 到图中 其他每个顶点 对应的最短路径


接下来我们来对算法步骤进行剖析,下面是一张网 以及对应的邻接矩阵

                 图 (Graph)                    邻接矩阵(Adjesent Matrix)
在这里插入图片描述


执行Step1初始化后,我们将会得到以下结果,由于从 V0开始,从 V0 到 V0 最短路径为零,故V0 率先加入到 集合 S[ ]中,标明已经求得最短路径。数组path[ ] 置为 -1,表示目前还没求得 V0 到 连线的顶点 的 路径前驱


执行Step2,找到距离 V0 最近的连线顶点 Vm,即 V1


将 V1 置入 集合 S[ ] 中,即 s[ V1]=1


相应地,我们可求得 V1 到 V2,V3,V4 路径长。在其基础上,更新修正 dist[ ] 中 V0 到 V2,V3,V4 的最短路径长,更新 path 中 相应的 当前 最短路径的前驱,例如 从 V0 到 V3 的当前最短路径前驱 是 V1,即 V0 -> V1 -> V3,故 path[ V3 ]=1

在这里插入图片描述


对标 Step2,我们每次都使用,上次所求得的节点中 距离 相对起点( 这里为 V1) 最近的节点作为下一个程序开始的相对起点 (这里为 V2),同样依次求 V2 到它有连线的顶点的距离(这里为 V4 和 V5 ),进而求得 V0 到 V4,V5 的对应的 新的最短路径长, 更新 path 中 相应的路径前驱以及集合S,此时我们会发现 path[ V4 ]=2,这是什么意思呢? 难道 是 从 V0 经过 V2 直接到达 V4 ,即( V0 -> V2 -> V4 )是当前最短路径吗?

其实我们可以这样 理解,要想知道 起点 V0 到 V2 的最短路径 ,势必会有 一个节点 作为 V2 的前驱,这里 即是 V1。那么同样地,要想 知道 V0 到 V4 的最短路径,也势必有一个节点 作为 V4 的前驱,只不过这时会有 一个顶点作为相对起点 (V1)。故此 上式 ( V0 -> V2 -> V4 )的完全体 应为 (V0 ->V1-> V2 -> V4)


同样,我们将 上一步求得 距离 相对起点 V2 最近的 V4 ,作为下一个相对起点求取 其 和有连线顶点的距离,并更新 距离数组 dist 和 前驱数组 path 中相应的值


重复 Step2 ~ Step4共 n-1 次,我们即可求得 起点 V0 到 终点 V8 的最短路径 :

在这里插入图片描述


2.代码

2.1 相关声明

#define GRAPH_INFINITY 65535 /* 无穷大 */
#define MAXVEX 20 /*最大图顶点*/
#define MAXEDGE 20 /*最大图边*/

typedef struct
{
	int vexs[MAXVEX];/* 顶点 */
	int arc[MAXVEX][MAXVEX];/* 邻接矩阵 */
	int numVertexes;/* 顶点数 */
    int numEdges;/* 边数 */
    
}graphNet;

typedef int Dist[MAXVEX];/*存储从起点V0到终点Vm的当前最短路径长度,即当前的权值之和 */
typedef int Path[MAXVEX];/*存储从起点V0到终点Vm的当前最短路径的前驱 */

2.2 有权图的建立函数定义

void creatGraphNet(graphNet *G)/* 构建图 */
{
    int ii,jj;
    
    
    /*输入 边数 和 顶点 数,这里 可用 printf()*/
    G->numVertexes=9;
    G->numEdges=16;
    
    
    /*初始化 图*/
    for(ii=0; ii<G->numsVertexes; i++)
    {
        G->vexs[i]=ii;
    }
    
    for( ii=0; i<G->numVertexes; ii++)
    {
        for(jj=0 ;jj<G->numVertexes; jj++)
        {
            if(ii==jj)
                G->arc[ii][jj]=0;
            else
                G->arc[ii][jj]=G->arc[ii][jj]=GRAPH_INFINITY;
        }
    }
    
    /*赋予 权值*/
    G->arc[0][1] = 1;
	G->arc[0][2] = 5;
	G->arc[1][2] = 3;
	G->arc[1][3] = 7;
	G->arc[1][4] = 5;

	G->arc[2][4] = 1;
	G->arc[2][5] = 7;
	G->arc[3][4] = 2;
	G->arc[3][6] = 3;
	G->arc[4][5] = 3;

	G->arc[4][6] = 6;
	G->arc[4][7] = 9;
	G->arc[5][7] = 5;
	G->arc[6][7] = 2;
	G->arc[6][8] = 7;

	G->arc[7][8] = 4;
    
    for (ii = 0; ii < G->numVertexes; ii++)
	{
		for (jj = ii; jj < G->numVertexes; jj++)
		{
			G->arc[jj][ii] = G->arc[ii][jj];
		}
	}
    
}

2.3 核心算法:迪杰斯特拉

void ShortestPath_Dijkstra(graphNet G, int v0, Dist* D, Path* P)
{
    int v, vm, k, min;
	int S[MAXVEX];/* S[vm]=1表示求得顶点v0至vm的最短路径 */
    
	for (v = 0; v < G.numVertexes; v++)    /* 初始化数据 */
	{
		S[v] = 0;			/* 全部顶点初始化为未知最短路径状态 */
		(*D)[v] = G.arc[v0][v];/* 将与v0点有连线的顶点加上权值 */
		(*P)[v] = -1;				/* 初始化路径数组P为-1  */
	}

	(*D)[v0] = 0;  /* v0至v0路径为0 */
	S[v0] = 1;    /* v0至v0求路径的最短路径 0 */
    
	/* 开始主循环,每次求得v0到某个v顶点的最短路径 */
	for (v = 1; v < G.numVertexes; v++)
	{
		min = GRAPH_INFINITY;    /* 当前所知离v0顶点的最近距离 */
		for (vm = 0; vm < G.numVertexes; vm++) /* 寻找离v0最近的顶点 */
		{
			if (!S[vm] && (*D)[vm] < min)
			{
				k = vm;
				min = (*D)[vm];    /* m顶点离v0顶点更近 */
			}
		}
		S[k] = 1;    /* 将目前找到的最近的顶点置入 集合 S */
		for (vm = 0; vm < G.numVertexes; vm++) /* 修正当前最短路径及距离 */
		{
			/* 如果经过v顶点的路径比现在这条路径的长度短的话 */
			if (!S[vm] &&G.arc[k][vm]<GRAPH_INFINITY && (min + G.arc[k][vm] < (*D)[vm]))
			{ 
                /*  说明找到了更短的路径,修改D[vm]和P[vm] */
				(*D)[vm] = min + G.arc[k][vm];  /* 修改当前路径长度 */
				(*P)[vm] = k;
			}
		}
	}
}

此算法讲解到这里就结束了,如有错误与不足之处,欢迎指出,大家共同进步!!

迪杰斯特拉(Dijkstra)算法是一种寻找图中两点间最短路径的经典算法,适用于无向图和有向图,特别是当边权表示距离、费用或其他成本时。算法的基本思想是从起点开始逐步探索邻接节点,并始终选择当前已访问节点到未访问节点中代价最小的一条边作为下一步的前进方向。 在MATLAB中实现迪杰斯特拉算法通常涉及以下几个步骤: 1. **初始化**:创建一个二维数组或矩阵来存储各顶点之间的距离,将所有初始值设置为无穷大,除了起点到自身的距离设置为0;创建一个布尔型数组记录哪些节点已经被处理过。 2. **选取最小距离节点**:从未被处理过的节点中选出距离起点最近的一个节点作为当前节点。 3. **更新距离**:对于当前节点的所有相邻节点,如果从起点通过当前节点到相邻节点的距离比之前记录的距离更小,则更新这个距离。 4. **标记已处理节点**:将当前节点标记为已经处理过。 5. **重复步骤2至4**,直到所有节点都被处理或找到目标节点。 MATLAB代码示例: ```matlab function [shortestPaths, processedNodes] = dijkstra(graphMatrix, startNode) % graphMatrix 是一个邻接矩阵,其中非零元素表示两个节点间的距离。 % startNode 是起始节点的位置。 % shortestPaths 和 processedNodes 分别返回最短路径矩阵和处理节点状态。 n = size(graphMatrix, 1); visited = false(n, 1); % 初始化未访问节点标志位 distances = inf(1, n); % 初始距离设为无穷大 distances(startNode) = 0; % 起始节点距离设为0 for i = 1:n-1 current = find(~visited & (distances == min(distances(~visited))), 1); visited(current) = true; for j = 1:n if ~visited(j) && graphMatrix(current, j) ~= 0 newDist = distances(current) + graphMatrix(current, j); if newDist < distances(j) distances(j) = newDist; end end end end shortestPaths = distances; processedNodes = visited; ``` **相关问题**: 1. **如何优化迪杰斯特拉算法**?在大数据集上运行时,可以考虑使用优先队列来加速查找下一个最短路径候选节点的过程。 2. **迪杰斯特拉算法与贝尔曼-福特算法的区别是什么**?贝尔曼-福特算法可以在存在负权重边的情况下求解最短路径,而迪杰斯特拉算法不支持负权重边。 3. **如何将迪杰斯特拉算法应用到实际问题中**?比如网络路由优化、地图导航系统中的路径规划等场景,都可以利用此算法来找到从源点到所有其他点的最短路径。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值