单源最短路径dag算法

介绍

    可以根据拓扑排序来计算有向无环图(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最短路径运行结果

dag最短路径运行结果

    下面来看看dag最短路径的图解过程:

dag最短路径运行过程图解

dag最短路径运行过程图解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Z小偉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值