版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/qq_37653144/article/details/83513386
对于最短路径问题,Dijkstra算法只能求解所有边的权值都是非负的加权有向图,而Bellman-Ford算法虽然可以求解有负权值边的图的最短路径,但效率并不高。对于有向无环图,下面这种基于Dijkstra和拓扑排序的算法可以在线性时间内解决单点最短路径问题,且能够处理负权重的边,甚至能够求出最长路径。
该算法的思想很简单:按照拓扑顺序放松顶点。因为每条边v→w都只会被放松一次,当v被放松时有:dis[w] <= dis[v] + e.weight。在算法结束前该不等式都会成立,因为dis[v]是不会变化的(因为按照拓扑顺序放松顶点,在v被放松后算法不会再处理任何指向v的边)而dis[w]只会变小。因此,在所有从s可达的顶点都被加入到树中后,最短路径的最优性条件也就成立了。
结合拓扑排序算法和Dijkstra算法来求解有向无环图的最短路径可以在和V+E成正比的时间内得出结果。
//带权有向图
struct EdgeWeightedDigraph
{
size_t V; //顶点数
size_t E; //边数
map<int, forward_list<tuple<int, int, double>> adj; //改进后的邻接表,tuple存储的是边集
}
struct Comp
{
bool operator()(tuple<int, int, double> &a, tuple<int, int, double> &b)
{
return get<2>(a) > get<2>(b);
}
};
vector<int> FindShortestPath(EdgeWeightedDigraph &g)
{
vector<int> edge(g.V);
//定义并初始化向量dis[]
vector<double> dis(g.V, DBL_MAX);
dis.at(0) = 0.0;
vector<int> topo = TopologicalSort(g);
//关于拓扑排序详见https://blog.csdn.net/qq_37653144/article/details/83215196
for (const int &i : topo)
{
//放松顶点
for (const auto &e : g.adj.at(i))
{
if (dis.at(get<0>(e)) + get<2>(e) < dis.at(get<1>(e)))
{
dis.at(get<1>(e)) = dis.at(get<0>(e)) + get<2>(e);
edge.at(g.V) = get<0>(e);
}
}
}
return edge;
}