求图中最短路径算法之Dijkstra算法——C++实现并优化

Dijkstra算法是一种比较经典的求图中最短路径算法,它是一种贪心算法,可以求出从源节点到图中其他所有节点的最短路径。适用范围:用于求有向或无向加权图中两点间的最短路径,其中边的权值不能为负。

最近重新学习了该算法,并用C++将其实现,同时对代码进行了优化,优化思路如下:

一般的Dijkstra算法实现代码,在每次将节点添加到S集合之前,都要在所有节点中进行搜索,找出代价最小的节点进行添加。这种方法效率低下,我们可以发现,有两类节点不是候选节点:1.已经添加到S集合中的节点;2.到源节点代价为无穷的节点。基于这个原因,我将添加到S集合的候选节点保存在一个链表中,并动态的更新,以达到提升程序效率的目的。

由于Dijkstra算法比较经典,在此不再赘述。下面给出源代码。

输入

第一行输入三个整数n,m,s,分别表示节点个数,边条数,源节点;接下来输入m行,每一行输入三个整数u,v,w,分别表示一条边的起点,终点,权值;为了便于程序的调试,可以从文件中读取数据;

输出

输出从源节点到其他节点的最短路径以及最小代价,如果不存在则输出:No path;

源代码:

#include 
#include 
#include 
#include 
using namespace std;
//邻接表中节点,每个节点与该节点对应的索引号指定一条边
struct Node
{
	int u; //边终点节点号
	int w; //边权值
	Node(int a, int b) :u(a), w(b){}
};
struct Record
{
	int pre; //路径中当前节点的前一节点
	int cost; //当前节点到源节点的最短路径代价
};
int n, m, s; //n表示图中节点个数,m表示图中边数,s表示源节点
vector> Adj; //图的邻接表
vector Path; //采用双亲表示法存储源节点到其他所有节点的最短路径信息
void Dijkstra()
{	
	vector isUsed(n, false); //向量某索引号对应的值为true,表示该索引号对应的节点
	//在S集合中
	list Assi; //Assi中存储着当前的候选节点
	Path.assign(n, Record());
	//路径信息初始化
	for (int i = 0; i < n; i++)
	{
		Path[i].pre = i;
		Path[i].cost = INT_MAX;
	}
	isUsed[s] = true;
	for (auto it = Adj[s].begin(); it != Adj[s].end(); it++)
	{
		Path[it->u].pre = s;
		Path[it->u].cost = it->w;
		Assi.push_back(it->u);
	}
	while (!Assi.empty())
	{
		list::iterator It;
		int minCost = INT_MAX;
		//从Assi中选择代价最小的节点加入到S集合中
		for (auto it = Assi.begin(); it != Assi.end(); it++)
		{
			if (minCost > Path[*it].cost)
			{
				minCost = Path[*it].cost;
				It = it;
			}
		}
		int u = *It;
		Assi.erase(It);
		isUsed[u] = true;
		//对与选中节点直接相连,并且不在S集合中的节点进行松弛操作
		//同时更新Assi的内容
		for (auto it = Adj[u].begin(); it != Adj[u].end(); it++)
		{
			if (isUsed[it->u]) continue;
			if (Path[it->u].cost == INT_MAX) Assi.push_back(it->u);
			if (Path[it->u].cost > minCost + it->w)
			{
				Path[it->u].cost = minCost + it->w;
				Path[it->u].pre = u;
			}
		}
	}
}
void Traverse(int k)
{
	if (Path[k].pre == k) { cout << k; return; }
	Traverse(Path[k].pre);
	cout << " " << k;
}
void Print()
{
	cout << "Result:\n";
	for (int i = 0; i < n; i++)
	{
		if (i == s) continue;
		cout << "From " << s << " to " << i << ": ";
		if (Path[i].cost == INT_MAX){ cout << "No path\n\n"; continue; }
		Traverse(i);
		cout << endl;
		cout << "Minimal Cost: " << Path[i].cost << endl << endl;
	}
}
int main()
{
	ifstream in("data.txt"); //从文件中读取图的信息
	in >> n >> m >> s;
	int u, v, w;
	Adj.assign(n, list());
	while (m--)
	{
		in >> u >> v >> w;
		Adj[u].push_back(Node(v, w));
	}
	in.close();

	Dijkstra();
	Print();

	system("pause");
	return 0;
}

当用Dijkstra算法求两点间的最短路径时,可以在S集合中添加了终节点后就终止程序,从而提高程序效率。

算法测试图:
求图中最短路径算法之Dijkstra算法——C++实现并优化_第1张图片
程序运行结果:

求图中最短路径算法之Dijkstra算法——C++实现并优化_第2张图片


你可能感兴趣的:(算法)