一、单源最短路径问题

1. 问题描述

  • 给定带权有向图G =(V,E),其中每条边的权是非负实数。(V是顶点集合,E是边集合)
  • 给定V中的一个顶点,称为
  • 计算从源到所有其它各顶点的最短路长度。这里路的长度是指路径上各边权之和。

2. 算法分析

  • Dijkstra算法:是解单源最短路径问题的贪心算法。

基本思想:

  • 一个顶点属于集合S,当且仅当从源到该顶点的最短路径长度已知。

  • 设置顶点集合S,并不断地作贪心选择来扩充这个集合。

  • 贪心策略:每次都从V-S中找出具有最短特殊路长的顶点u加入S。

算法思路:

  1. 初始时,S中仅含有源点
  2. 设u是G的某一个顶点,把从源点到u且中间只经过S中顶点的路称为从源点到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。
  3. Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。
  4. 一旦S包含了V中所有顶点,dist就记录了从源到其它所有顶点之间的最短路径长度。

举个例子:

  • 对下图中的有向图,应用Dijkstra算法计算从源顶点1到其他顶点间最短路径的过程列在下面的表中。
    示例-有向图示例-表

[注]:有向图 --> 邻接矩阵

  • 用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵;

  • 每条有向边的起点i为行标,终点j为列标,权为值,存入矩阵第i行,第j列;
    矩阵其余项为∞。

  • 以上例中的有向图为例:
    邻 接 矩 阵 A = [ ∞ 10 ∞ 30 100 ∞ ∞ 50 ∞ ∞ ∞ ∞ ∞ ∞ 10 ∞ ∞ 20 ∞ 60 ∞ ∞ ∞ ∞ ∞ ] 邻接矩阵 A=\begin{bmatrix} \infty & 10 & \infty & 30 & 100 \\ \infty & \infty & 50 & \infty & \infty \\ \infty & \infty & \infty & \infty & 10 \\ \infty & \infty & 20 & \infty & 60 \\ \infty & \infty & \infty & \infty & \infty \\ \end{bmatrix} A=105020301001060

二、算法实现

1. 贪心算法

//【贪心算法】单源最短路径问题
#include <iostream>
using namespace std;

#define N 5		// 5个顶点,1、2、3、4、5
#define M 9999	// maxint,大整数

void Dijkstra(int n, int v, int dist[], int prev[], int c[][N + 1]);
void Traceback(int v, int i, int prev[]);

int main()
{
	int v = 1;			// 源点为1
	int dist[N + 1];	// 从源到顶点i的最短特殊路径长度
	int	prev[N + 1];	// 从源到顶点i的最短路径上前一个顶点
	// 带权有向图的邻接矩阵,行和列下标从1开始
	int c[N + 1][N + 1] = {
		{M,	M,	M,	M,	M,	M	},
		{M,	M,	10,	M,	30,	100	},
		{M,	M,	M,	50,	M,	M	},
		{M,	M,	M,	M,	M,	10	},
		{M,	M,	M,	20,	M,	60	},
		{M,	M,	M,	M,	M,	M	},
	};

	// “输入”:带权有向图
	cout << "带权有向图的邻接矩阵为:\n";
	for (int i = 1; i <= N; i++)
	{
		for (int j = 1; j <= N; j++)
			cout << c[i][j] << "\t";
		cout << endl;
	}

	// Dijkstra算法
	Dijkstra(N, v, dist, prev, c);

	// 输出
	cout << "源点1到顶点5的最短路径长度为:" << dist[5];
	cout << ",路径为:";
	Traceback(1, 5, prev);
	cout << endl;

	return 0;
}


void Dijkstra(int n, int v, int dist[], int prev[], int c[][N + 1])
{
	bool s[N + 1];	// 顶点集合s
	for (int i = 1; i <= n; i++)
	{
		dist[i] = c[v][i];	// 从源到顶点i的最短特殊路径长度
		s[i] = false;

		if (dist[i] == M)
			prev[i] = 0;	// 从源到顶点i的最短路径上前一个顶点
		else
			prev[i] = v;
	}

	dist[v] = 0;
	s[v] = true;

	for (int i = 1; i < n; i++)
	{
		int temp = M;		// 
		int u = v;			// 上一顶点

		// 找到具有最短特殊路长度的顶点u
		for (int j = 1; j <= n; j++)
		{
			if ((!s[j]) && (dist[j] < temp))
			{
				u = j;
				temp = dist[j];
			}
		}
		s[u] = true;

		// 更新dist值
		for (int j = 1; j <= n; j++)
		{
			if ((!s[j]) && (c[u][j] < M))
			{
				int newdist = dist[u] + c[u][j];
				if (newdist < dist[j])
				{
					dist[j] = newdist;
					prev[j] = u;
				}
			}
		}
	}
}

//输出最短路径 v源点,i终点,
void Traceback(int v, int i, int prev[])
{
	// 源点等于终点时,即找出全部路径
	if (v == i)
	{
		cout << i;
		return;
	}
	Traceback(v, prev[i], prev);
	cout << "->" << i;
}

2. 运行结果展示

运行截图

三、友情链接~


最后,非常欢迎大家来讨论指正哦!

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐