熟悉图的人可以知道,对于单源最短路径的问题,我们可以用bellman-ford算法,或者dijkstra算法来解决,bellman-ford可以解决 有向无环图中边的权值为负数的情况,但是dijkstra不能解决复权值的问题。如果给定一个图G (v, e), bellman-ford求最短路径的时间复杂度是O(ve), 而dijkstra所用的时间是O(vlogv)。对于这两种方法就不介绍了。下面介绍一种应用拓扑排序的来解决单源最短路径的问题。时间复杂度是O(v+e),想了很久,为什么要用拓扑排序能够很好的解决单源最短路径的问题呢。大家想一想拓扑排序的规则:不断的寻找入度为0的节点,那么入度为0的节点代表什么呢,入度为0的节点说明在它入度变为0之前,所有指向它的节点已经通过邻接关系访问过它,也就是说,如果以拓扑排序访问的顺序来计算最短路径的话,那么给定一个节点,只有在拓扑排序顺序排在它之前的节点才有边指向这个节点,排在它之后的就可以忽略掉了,这样就节省了遍历的时间,而且保证不会漏掉任何一个指向它的节点。在遍历到这个节点之前,就已经计算好某个节点,到这个节点的单源最短路值。这就是应用拓扑排序的最大精妙之处。
拓扑排序设定规则如下:如果给定一个图中含有V个节点,初始化一个int数组,dist[V], 将指定的源点的值设置为0之外,其他的都设置成无穷大。之后进行拓扑排序,按照拓扑排序的顺序处理每个节点到邻接点的距离。下面给个具体的例子:
之后给出代码:
#include<iostream>
#include<list>
#include<stack>
#include<limits.h>
using namespace std;
#define INF INT_MAX
class AdjListNode {
int v;
int weight;
public:
AdjListNode(int _v, int _weight) {
v = _v;
weight = _weight;
}
int getV() {
return v;
}
int getWeight() {
return weight;
}
};
class Graph {
int vertexNum;
list<AdjListNode> *adjacents;
public:
Graph(int _vertexNum) {
vertexNum = _vertexNum;
adjacents = new list<AdjListNode>[vertexNum];
}
void topologicalUtil(int v, bool *visited, stack<int> &m_stack);
void addEdge(int u, int v, int weight);
void shortestPath(int s);
};
void Graph::addEdge(int u, int v, int weight) {
AdjListNode node = AdjListNode(v, weight);
adjacents[u].push_back(node);
}
void Graph::topologicalUtil(int v, bool *visited, stack<int> &m_stack) {
visited[v] = true;
list<AdjListNode>::iterator iter;
for (iter = adjacents[v].begin(); iter != adjacents[v].end(); iter++) {
if (false == visited[iter->getV()])
topologicalUtil(iter->getV(), visited, m_stack);
}
m_stack.push(v);
}
void Graph::shortestPath(int s) {
stack<int> m_stack;
bool *visited = new bool[vertexNum];
int v, u;
for (v = 0; v < vertexNum; v++)
visited[v]= false;
int *dist = new int[vertexNum];
for (v = 0; v < vertexNum; v++)
dist[v] = INF;
for (v = 0; v < vertexNum; v++)
if (false == visited[v])
topologicalUtil(v, visited, m_stack);
dist[s] = 0;
while (!m_stack.empty()) {
u = m_stack.top();
m_stack.pop();
list<AdjListNode>::iterator iter;
if (INF != dist[u]) {
for (iter = adjacents[u].begin(); iter != adjacents[u].end(); iter++) {
v = iter->getV();
if (dist[v] > dist[u] + iter->getWeight())
dist[v] = dist[u] + iter->getWeight();
}
}
}
for (v = 0; v < vertexNum; v++) {
if (INF == dist[v])
cout << "INF" << " ";
else
cout << dist[v] << " ";
}
}
int main(int argc, char *argv[]) {
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);
int s = 1;
cout << "Following are shortest distances from source " << s <<" \n";
g.shortestPath(s);
cin.get();
return 0;
}
Following are shortest distances from source 1 INF 0 2 6 5 3