介绍
可以根据拓扑排序来计算有向无环图(dag)的单源最短路径,因为拓扑排序正好是建立在无环的基础上,在这个图中没有负权重边以及负权重回路边。
实现
因为实现是基于拓扑排序的,所以基本结构和拓扑排序一致,只是多了一个距离的定义:
enum COLOR
{
WHITE = 0,
GRAY,
BLACK
};
typedef struct _Vertex
{
char color;
int vertex;
int start;
int finish;
int distance;
}Vertex;
typedef struct _Node
{
Vertex *pVertex;
struct _Node *parent;
struct _Node *next;
}Node;
typedef struct Edge_
{
int x;
int y;
bool operator == (const struct Edge_& l)
{
return x == l.x && y == l.y;
}
}Edge;
bool operator < (const Edge& r, const Edge& l)
{
if (r.x == l.x)
{
return r.y < l.y;
}
return r.x < l.x;
}
static map<Edge, int> g_edges;
static int g_time = 0;
static list<Node*> g_Nodes;
Node* initNode(Node **graph, int vertex)
{
Node *p = new Node;
p->pVertex = graph[vertex - 1]->pVertex;
p->parent = nullptr;
p->next = nullptr;
return p;
}
void InsertVertex(Node **graph, Node *node, int vertex, int weight)
{
Node *p = node;
while (nullptr != p->next)
{
p = p->next;
}
p->next = initNode(graph, vertex);
Edge edge;
edge.x = node->pVertex->vertex;
edge.y = vertex;
g_edges.insert(std::pair<Edge, int>(edge, weight));
}
void initGraph(Node **list, int vertexCount)
{
for (int i = 0; i < vertexCount; ++ i)
{
list[i] = new Node;
list[i]->parent = nullptr;
list[i]->next = nullptr;
list[i]->pVertex = new Vertex;
list[i]->pVertex->color = WHITE;
list[i]->pVertex->vertex = i + 1;
}
}
void depthFirstSearchVisit(Node **graph, Node *node)
{
++ g_time;
node->pVertex->start = g_time;
node->pVertex->color = GRAY;
Node *p = graph[node->pVertex->vertex - 1];
while (nullptr != p)
{
if (WHITE == p->pVertex->color)
{
p->parent = node;
depthFirstSearchVisit(graph, p);
}
p = p->next;
}
node->pVertex->color = BLACK;
++ g_time;
node->pVertex->finish = g_time;
g_Nodes.push_front(node);
}
int getSentinel()
{
return numeric_limits<int>::max();
}
void depthFirstSearch(Node **graph, int vertexCount)
{
for (int i = 0; i < vertexCount; ++ i)
{
if (WHITE == graph[i]->pVertex->color)
{
depthFirstSearchVisit(graph, graph[i]);
}
}
}
void initializeSingleSource(Node **graph, Node *root)
{
for (int i = 0; i < VERTEX_NUMBER; ++ i)
{
graph[i]->pVertex->distance = getSentinel();
graph[i]->parent = nullptr;
}
root->pVertex->distance = 0;
}
void relax(Node *u, Node *v, int weight)
{
if (v->pVertex->distance > u->pVertex->distance + weight)
{
v->pVertex->distance = u->pVertex->distance + weight;
v->parent = u;
}
}
这里的初始化和松弛是bellman算法中的实现。
下面来看看dag的实现:
int w(int x, int y)
{
Edge edge;
edge.x = x;
edge.y = y;
return g_edges[edge];
}
void topologicalSort(Node **graph)
{
depthFirstSearch(graph, VERTEX_NUMBER);
for (int i = 0; i < VERTEX_NUMBER; ++ i)
{
cout << "vertex " << i + 1
<< ",start = " << graph[i]->pVertex->start
<< ",finish = " << graph[i]->pVertex->finish;
cout << endl;
}
for (auto iter = g_Nodes.begin(); iter != g_Nodes.end(); ++ iter)
{
cout << (*iter)->pVertex->vertex << " ";
}
cout << endl;
}
void dagShortestPath(Node **graph)
{
topologicalSort(graph);
initializeSingleSource(graph, graph[0]);
cout << "dag shortest path: " << endl;
for (auto iter = g_Nodes.begin(); iter != g_Nodes.end(); ++ iter)
{
int vertex = (*iter)->pVertex->vertex;
Node *p = graph[vertex - 1]->next;
while (nullptr != p)
{
relax(graph[vertex - 1], graph[p->pVertex->vertex - 1], w(vertex, p->pVertex->vertex));
p = p->next;
}
}
}
从上面的实现中可以看出,dag中最短路径的算法是,先进行拓扑排序,然后根据拓扑排序,在对每个顶点的邻接表进行松弛技术,等算法结束,最短路径也已求出。
对上面的代码进行测试:
int _tmain(int argc, _TCHAR* argv[])
{
copyright();
cout << "directed acylic graph: " << endl;
Node *graph[VERTEX_NUMBER] = {0};
initGraph(graph, VERTEX_NUMBER);
InsertVertex(graph, graph[0], 2, 6);
InsertVertex(graph, graph[0], 4, 7);
InsertVertex(graph, graph[1], 3, 5);
InsertVertex(graph, graph[1], 4, 8);
InsertVertex(graph, graph[1], 5, -4);
InsertVertex(graph, graph[2], 2, -2);
InsertVertex(graph, graph[3], 3, -3);
InsertVertex(graph, graph[3], 5, 9);
InsertVertex(graph, graph[4], 1, 2);
InsertVertex(graph, graph[4], 3, 7);
cout << "depth First Search:" << endl;
dagShortestPath(graph);
for (int i = 0; i < VERTEX_NUMBER; ++ i)
{
if (nullptr != graph[i]->parent)
{
cout << "edge:" << graph[i]->parent->pVertex->vertex
<< "," << graph[i]->pVertex->vertex << endl;
}
}
return 0;
}
下面来看看dag最短路径的图解过程: