本文是基于带权有向无环图最短路径的实现,必需是无环的(有环不存在拓扑排序)
一、预备知识:
拓扑排序:有向图的拓扑排序是其顶点的线性排序,使得对于从顶点u 到顶点v的每个有向uv,u 在排序中都在v之前
可以看这个https://blog.csdn.net/qq_40691051/article/details/103141074
二、算法原理:
为什么要拓扑排序?
因为如果给定存在拓扑排序(给定图没环),那么如果有边u--->v
,那么u
在拓扑排序里肯定排在v
前面,反过来u
排在v
前面,说明从u
是可能到v
的,对于缩短距离是有帮助的,所以只要按拓扑排序来遍历各顶点的出边,检查自己对于缩短从src
到出边的目标顶点的距离有没有帮助就行了。
实现代码:
以下代码实现了计算每对顶点间的距离,因为拓扑排序只需要计算一次就能够了,所以存起来了。
#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]: 5
从1出发的路径:
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]: 3
从2出发的路径:
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]: 2
从3出发的路径:
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]: -3
从4出发的路径:
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]: -2
从5出发的路径:
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 . . .