图算法 -- Dijkstra Algorithm

本文详细介绍了如何使用C语言实现Dijkstra算法,并通过实例解释了算法的执行过程及核心思想,帮助读者理解如何在图论问题中寻找最短路径。

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

<h2 align="center"><strong><em>The Dijkstra Algorithm</em></strong></h2><p align="center"><a target=_blank href="mailto:eryar@163.com"><strong><em>eryar</em></strong>@<strong><em>163</em></strong>.<strong><em>com</em></strong></a> </p><p><strong>摘要</strong>:本文用<strong><em>C</em></strong>实现了图的最短路径<strong><em>Dijkstra</em></strong>算法,并将自己理解该算法的方式与大家分享下,若有错误之处,欢迎指正。 </p><p><strong>关键字</strong>:图、最短路径、<strong><em>Graph</em></strong>、<strong><em>Dijkstra</em></strong> </p><h3>一、引言 <strong><em>Introduction</em></strong></h3><p>对图<strong><em>G</em></strong>中的每一条边<strong><em>e</em></strong>都赋以一个实数<strong><em>w</em></strong>(<strong><em>e</em></strong>),则<strong><em>G</em></strong>连同它边上的权称为赋权图。赋权图经常出现在图论的实际应用问题中,边上的权(<strong><em>Weight</em></strong>)可以表示各种实际意义,如在输送网络中,权可以表示两地的距离,可以表示运输的时间,还可以表示运输的费用等。许多最优化问题相当于在一个赋权图中找出某类具有最小(或最大)权的子图。例如,给出一个连接各城镇的铁路网,要找出一条给定两个城镇间的最短路线。这个问题的图论模型建立如下:以顶点代表城镇,边代表城镇间的铁路线,权表示直接相连的城镇之间的铁路距离,得到一个赋权图,即网络<strong><em>N</em></strong>。 </p><p>本文讨论带权的有向图,并称路径上的第一个顶点为源点(<strong><em>Source</em></strong>),最后一个顶点为终点(<strong><em>Destination</em></strong>)。 </p><p align="center"><a target=_blank href="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/Dijkstra_Animation_4.gif"><img style="DISPLAY: block; FLOAT: none; MARGIN-LEFT: auto; MARGIN-RIGHT: auto" title="Dijkstra_Animation" alt="Dijkstra_Animation" src="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/Dijkstra_Animation_thumb_1.gif" width="283" height="222" /></a> </p><h3>二、算法理解 <strong><em>Understanding the Algorithm</em></strong></h3><p><strong><em>Dijkstra</em></strong>算法描述为:假设用带权邻接矩阵来表示带权有向图。首先引进一个辅助向量<strong><em>D</em></strong>,它的每个分量<strong><em>D</em></strong>[<strong><em>i</em></strong>]表示当前所找到的从始点<strong><em>v</em></strong>到每个终点<strong><em>Vi</em></strong>的最短路径。它的初始状态为:若两顶点之间有弧,则<strong><em>D</em></strong>[<strong><em>i</em></strong>]为弧上的权值;否则置<strong><em>D</em></strong>[<strong><em>i</em></strong>]为无穷大。 </p><p>u 找到与源点<strong><em>v</em></strong>最近的顶点,并将该顶点并入最终集合<strong><em>S</em></strong>; </p><p>u 根据找到的最近的顶点更新从源点<strong><em>v</em></strong>出发到集合<strong><em>V</em></strong>-<strong><em>S</em></strong>上可达顶点的最短路径; </p><p>u 重复以上操作。 </p><p><a target=_blank href="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/clip_image004_2.jpg"><img style="BORDER-BOTTOM: 0px; BORDER-LEFT: 0px; DISPLAY: inline; BORDER-TOP: 0px; BORDER-RIGHT: 0px" title="" border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/eryar/WindowsLiveWriter/Dijkstra_11DE9/clip_image004_thumb.jpg" width="243" height="234" /></a> </p><p>图<strong><em>1</em></strong> 带权有向图 </p><p>以前总是认为<strong><em>Dijkstra</em></strong>算法可以用来求从源点到指定终点的最短路径,导致总不能抓住算法的中心思想。现在认为把握<strong><em>Dijkstra</em></strong>的算法要点为: </p><p>u <strong><em>Dijkstra</em></strong>提出了一个按路径长度递增的次序产生最短路径的算法; </p><p>u 每次循环都可以得到一个从源点到某个顶点的最短路径,某个即不是确定的一个; </p><p>以带权有向图<strong><em>1</em></strong>为例说明<strong><em>Dijkstra</em></strong>算法的执行过程:假设源点为<strong><em>v0</em></strong>,则初始状态时源点到其它各顶点的距离为: </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em>10</em></strong></p></td><td valign="top" width="95"><p>∽</p></td><td valign="top" width="95"><p><strong><em>30</em></strong></p></td><td valign="top" width="95"><p><strong><em>100</em></strong></p></td></tr></tbody></table><p>由上表可知,与源点<strong><em>v0</em></strong>最近的顶点为<strong><em>v2</em></strong>,距离为<strong><em>10</em></strong>。 </p><p>将<strong><em>v2</em></strong>加入到最终顶点集合<strong><em>S</em></strong>中。 </p><p>再根据<strong><em>v2</em></strong>更新从源点到其它顶点的最短距离,即从<strong><em>v0</em></strong>-<strong><em>v2</em></strong>-<strong><em>v3</em></strong>的距离为<strong><em>60</em></strong><∽,所以将<strong><em>v0</em></strong>到<strong><em>v3</em></strong>的距离更新为<strong><em>60</em></strong>,如下表所示: </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em><del>10</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>60</em></strong></p></td><td valign="top" width="95"><p><strong><em>30</em></strong></p></td><td valign="top" width="95"><p><strong><em>100</em></strong></p></td></tr></tbody></table><p>由上表可知,与源点<strong><em>v0</em></strong>次近的顶点为<strong><em>v4</em></strong>,距离为<strong><em>30</em></strong>。 </p><p>将<strong><em>v4</em></strong>加入到最终顶点集合<strong><em>S</em></strong>中; </p><p>再根据<strong><em>v4</em></strong>更新从源点到其它顶点的最短距离。即从<strong><em>v0</em></strong>-<strong><em>v4</em></strong>-<strong><em>v3</em></strong>的距离为<strong><em>50</em></strong><<strong><em>60</em></strong>,所以将<strong><em>v0</em></strong>到<strong><em>v3</em></strong>的距离更新为<strong><em>50</em></strong>;从<strong><em>v0</em></strong>-<strong><em>v4</em></strong>-<strong><em>v5</em></strong>的距离为<strong><em>90</em></strong><<strong><em>100</em></strong>,所以将<strong><em>v0</em></strong>到<strong><em>v5</em></strong>的距离更新为<strong><em>90</em></strong>。 </p><table border="1" cellspacing="0" cellpadding="0"><tbody><tr><td valign="top" width="103"><p>源点 终点</p></td><td valign="top" width="86"><p><strong><em>v1</em></strong></p></td><td valign="top" width="95"><p><strong><em>v2</em></strong></p></td><td valign="top" width="95"><p><strong><em>v3</em></strong></p></td><td valign="top" width="95"><p><strong><em>v4</em></strong></p></td><td valign="top" width="95"><p><strong><em>v5</em></strong></p></td></tr><tr><td valign="top" width="103"><p><strong><em>v0</em></strong></p></td><td valign="top" width="86"><p>∽</p></td><td valign="top" width="95"><p><strong><em><del>10</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>50</em></strong></p></td><td valign="top" width="95"><p><strong><em><del>30</del></em></strong><del></del></p></td><td valign="top" width="95"><p><strong><em>90</em></strong></p></td></tr></tbody></table><p>重复以上操作…… </p><p>直到最终集合包含了所有的顶点。</p>
/*Dijkstra求单源最短路径 2010.8.26*/
#include<fstream>
#include <iostream>
#include<stack>
#define M 100
#define N 100
using namespace std;

typedef struct node
{
	int matrix[N][M];      //邻接矩阵 
	int n;                 //顶点数 
	int e;                 //边数 
}MGraph;

void DijkstraPath(MGraph g, int *dist, int *path, int v0)   //v0表示源顶点 
{
	int i, j, k;
	bool *visited = (bool *)malloc(sizeof(bool)*g.n);
	for (i = 0; i<g.n; i++)     //初始化 
	{
		if (g.matrix[v0][i]>0 && i != v0)
		{
			dist[i] = g.matrix[v0][i];
			path[i] = v0;     //path记录最短路径上从v0到i的前一个顶点 
		}
		else
		{
			dist[i] = INT_MAX;    //若i不与v0直接相邻,则权值置为无穷大 
			path[i] = -1;
		}
		visited[i] = false;
		path[v0] = v0;
		dist[v0] = 0;
	}
	visited[v0] = true;
	for (i = 1; i<g.n; i++)     //循环扩展n-1次 
	{
		int min = INT_MAX;
		int u;
		for (j = 0; j<g.n; j++)    //寻找未被扩展的权值最小的顶点 
		{
			if (visited[j] == false && dist[j]<min)
			{
				min = dist[j];
				u = j;
			}
		}
		visited[u] = true;
		for (k = 0; k<g.n; k++)   //更新dist数组的值和路径的值 
		{
			if (visited[k] == false && g.matrix[u][k]>0 && min + g.matrix[u][k]<dist[k])
			{
				dist[k] = min + g.matrix[u][k];
				path[k] = u;
			}
		}
	}
}

void showPath(int *path, int v, int v0)   //打印最短路径上的各个顶点 
{
	stack<int> s;
	int u = v;
	while (v != v0)
	{
		s.push(v);
		v = path[v];
	}
	s.push(v);
	while (!s.empty())
	{
		cout << s.top() << " ";
		s.pop();
	}
}

int main(int argc, char *argv[])
{
	int n, e;     //表示输入的顶点数和边数 
	fstream cin;
	cin.open("in16.txt");
	while (cin >> n >> e&&e != 0)
	{
		int i, j;
		int s, t, w;      //表示存在一条边s->t,权值为w
		MGraph g;
		int v0;
		int *dist = (int *)malloc(sizeof(int)*n);
		int *path = (int *)malloc(sizeof(int)*n);
		for (i = 0; i<N; i++)
		for (j = 0; j<M; j++)
			g.matrix[i][j] = 0;
		g.n = n;
		g.e = e;
		for (i = 0; i<e; i++)
		{
			cin >> s >> t >> w;
			g.matrix[s][t] = w;
		}
		cin >> v0;        //输入源顶点 
		DijkstraPath(g, dist, path, v0);
		for (i = 0; i<n; i++)
		{
			if (i != v0)
			{
				showPath(path, i, v0);
				cout << dist[i] << endl;
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值