带权有向无环图的最短路径(使用拓扑排序的方法)

22 篇文章 0 订阅
13 篇文章 1 订阅

本文是基于带权有向无环图最短路径的实现,必需是无环的(有环不存在拓扑排序)

一、预备知识:

拓扑排序:有向图的拓扑排序是其顶点的线性排序,使得对于从顶点u 到顶点v的每个有向uv,u 在排序中都在v之前

可以看这个https://blog.csdn.net/qq_40691051/article/details/103141074

二、算法原理:

为什么要拓扑排序?

因为如果给定存在拓扑排序(给定图没环),那么如果有边u--->v,那么u在拓扑排序里肯定排在v前面,反过来u排在v前面,说明从u是可能到v的,对于缩短距离是有帮助的,所以只要按拓扑排序来遍历各顶点的出边,检查自己对于缩短从src到出边的目标顶点的距离有没有帮助就行了。
https://www.geeksforgeeks.org/shortest-path-for-directed-acyclic-graphs/

实现代码:

以下代码实现了计算每对顶点间的距离,因为拓扑排序只需要计算一次就能够了,所以存起来了。

#include<iostream>
#include<list>
#include<vector>
#include<queue>
#include<set>
#include<stack>
#include<memory.h> 

#define INF 9999999

using namespace std;

class Graph
{
	int n;
	double **dist;		        	//存距离 
	int **paths;		   	    	//存路径
	int *topoLogicalOrder; 	    	//存所有节点的拓扑排序 
	list<pair<int,double> >*adj;	//邻接表 
	void topoLogicalSortUtil(int u,bool visited[],stack<int>&stk);	//求拓扑排序 
	void printSolution();		 	//打印解 
public:
	 Graph(int _n);	
	 void addEdge(int u, int v, double weight)	
	 {
	 	adj[u].push_back(make_pair(v, weight)); 
	 }
	 void shortestPath(); 
};
Graph::Graph(int _n)		//构造函数 
{
	n = _n;
	adj = new list<pair<int, double> >[_n];
	topoLogicalOrder = new int[_n];
	dist = new double*[_n];
	paths = new int*[_n];
	
	for(int i = 0; i < _n; i++)
	{
		dist[i] = new double[_n];
		paths[i] = new int[_n];
		for(int j = 0; j < _n; j++)
		{
			dist[i][j] = INF;
			paths[i][j] = i; 
		}
	} 
} 
void Graph::printSolution()
{
	for(int i = 0; i < n; i++)
	{ 
		cout<<"从"<<i<<"出发的路径:"<<endl; 
		for(int j = 0; j < n; j++)
		{
			cout<<paths[i][j]<<"--->"<<j<<"\t dist["<<i<<"]["<<j<<"]: "; 
			(dist[i][j] == INF) ? cout<<"INF"<<endl : cout<<dist[i][j]<<endl; 
		}
		cout<<endl;
	}
} 
void Graph::topoLogicalSortUtil(int u,bool visited[],stack<int>&stk)
{//将得到一组拓扑排序 
	visited[u] = true;
	
	list<pair<int, double> >::iterator it;
	for(it = adj[u].begin(); it != adj[u].end(); it++)
	{
		int v = it->first;
		if(visited[v] == false)
		{
			topoLogicalSortUtil(v, visited, stk); 
		} 
	} 
	stk.push(u);
} 
void Graph::shortestPath()
{ 
	stack<int>stk;
	bool visited[n];
	for(int i = 0; i < n; i++)
	{
		visited[i] = false; 
	}
	for(int i = 0; i < n; i++)
	{
		if(visited[i] == false)
		{
			topoLogicalSortUtil( i, visited, stk);
		}
	} 
	for(int i = 0; i < n; i++)
	{//把拓扑排序存起来 
		topoLogicalOrder[i] = stk.top();
		stk.pop(); 
	} 
	list<pair<int, double> >::iterator it;
	for(int i = 0; i < n; i++)
	{//从第i个顶点到其他顶点的最短路径 
		dist[i][i] = 0; 
		for(int j = 0; j < n; j++)
		{//j用来控制顶点的活动顺序(在拓扑排序里的顺序) 
			int u = topoLogicalOrder[j];	
			if(dist[i][u] != INF)
			{//在有向无环图里,如果dist[i][u]==INF,说明在拓扑排序里,
			//u排在i前面,i必然到不了u,所以更新不了u的出度顶点的dist 
				for(it = adj[u].begin(); it != adj[u].end(); it++)
				{
					int v = it->first;
					double weight = it->second;
					if(dist[i][v] > dist[i][u] + weight)
					{
						dist[i][v] = dist[i][u] + weight;
						paths[i][v] = u;
					}
				}
			}
		}
	} 
	printSolution();
} 
int main()
{    
    Graph g(6); 
    g.addEdge(0, 1, 5); 
    g.addEdge(0, 2, 3); 
    g.addEdge(1, 3, 6); 
    g.addEdge(1, 2, 2); 
    g.addEdge(2, 4, 4); 
    g.addEdge(2, 5, 2); 
    g.addEdge(2, 3, 7); 
    g.addEdge(3, 4, -1); 
    g.addEdge(4, 5, -2);
    
    g.shortestPath();
    return 0;
}

输出:

0出发的路径:
0--->0   dist[0][0]: 0
0--->1   dist[0][1]: 5
0--->2   dist[0][2]: 3
2--->3   dist[0][3]: 10
2--->4   dist[0][4]: 7
2--->5   dist[0][5]: 51出发的路径:
1--->0   dist[1][0]: INF
1--->1   dist[1][1]: 0
1--->2   dist[1][2]: 2
1--->3   dist[1][3]: 6
3--->4   dist[1][4]: 5
4--->5   dist[1][5]: 32出发的路径:
2--->0   dist[2][0]: INF
2--->1   dist[2][1]: INF
2--->2   dist[2][2]: 0
2--->3   dist[2][3]: 7
2--->4   dist[2][4]: 4
2--->5   dist[2][5]: 23出发的路径:
3--->0   dist[3][0]: INF
3--->1   dist[3][1]: INF
3--->2   dist[3][2]: INF
3--->3   dist[3][3]: 0
3--->4   dist[3][4]: -1
4--->5   dist[3][5]: -34出发的路径:
4--->0   dist[4][0]: INF
4--->1   dist[4][1]: INF
4--->2   dist[4][2]: INF
4--->3   dist[4][3]: INF
4--->4   dist[4][4]: 0
4--->5   dist[4][5]: -25出发的路径:
5--->0   dist[5][0]: INF
5--->1   dist[5][1]: INF
5--->2   dist[5][2]: INF
5--->3   dist[5][3]: INF
5--->4   dist[5][4]: INF
5--->5   dist[5][5]: 0


--------------------------------
Process exited with return value 0
Press any key to continue . . .
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值