图的拓扑排序、关键路径、最短路径算法 -- C++实现

一:拓扑排序

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。

拓扑排序就是要找到入度为0的顶点,并依次压栈。首先将栈顶顶点输出,删除以此顶点为弧尾的顶点,并更新其他顶点的入度。若入度为0,则继续压栈。更新完毕继续出栈,直至栈空。元素出栈并输出的顺序即为拓扑排序结果。我们代码中会利用数组模拟一个栈空间。

如图所示:

该图所得到拓扑排序结果为:F A D C  E B。

算法如下:

template <typename T, typename E>
void graph_mtx<T, E>::topological_sort()
{
	int num = this->get_numvertices();
	assert(num != -1);

	int *count = new int[num];
	assert(count != nullptr);

	for(int i=0; i<num; ++i)
		for(int j=0; j<num; ++j){
			if(edge[i][j] != 0 && edge[i][j] != MAX_COST)
				count[j]++;	//统计入度
		}

	int top = -1;
	for(int i=0; i<num; ++i)
		if(count[i] == 0){   //利用数组模拟栈
			count[i] = top;     //相当于压栈,存储之前位置
			top = i;            //存储当前位置
		}

	for(int i=0; i<num; ++i){
		if(top == -1)    //如果为-1,说明有环存在,回到了起始位置
			return ;

		int index_tmp = top;
		top = count[top];   //取栈顶元素

		cout << get_vertex_symbol(index_tmp) << "  ";   //输出栈顶元素

		int neighbor_index = get_firstneighbor(get_vertex_symbol(index_tmp));
		while(neighbor_index != -1){
			if(--count[neighbor_index] == 0){       //如果入度减为0,入栈
				count[neighbor_index] = top;
				top = neighbor_index;
			}
			neighbor_index = get_nextneighbor(get_vertex_symbol(index_tmp),
											  get_vertex_symbol(neighbor_index));
		}
	}

	delete []count;
}

二:关键路径

关键路径:从源点到汇点的路径长度最长的路径叫关键路径。

图中a(1..n)表示两活动之间花费的时间。

如图所示:

图中所求关键路径为A B E G I 或 A B E H I。

两个重要概念:

1.最早开始时间

如图E,从A到E所花费的时间在两条路径上飞别为7和5,那么E活动什么时候开始呢?毫无疑问,由于E活动开始存在B、C时间两个前提,所以在工程起始时间一定的情况下,A到E最少需要花费时间取决于ABE这条较长分支,这条分支如果未完成,那么C就算完成了也只能等待另一分支完成,此时E不能开始。由此可见,最早开始时间即为从源点到目标顶点最长的路径花费的时间。

2.最晚开始时间

同理,最晚开始时间是从汇点倒着往前计算的。与上面情况相同,最晚开始时间取决于从后往前经过的路径长度最长的分支。

由图易知,最早开始时间和最晚开始时间相等的顶点,即为关键路径顶点。

算法如下:

template <typename T, typename E>
void graph_mtx<T, E>::critical_path(const T& vert)
{
	int num = this->get_numvertices();

	int *early_start = new int[num];   //最早开始时间数组
	int *late_start = new int[num];    //最晚开始时间数组
	assert(early_start != nullptr && late_start != nullptr);

	int index = get_vertex_index(vert);
	assert(index != -1);

	for(int i=0; i<num; ++i)
		early_start[i] = 0;      //初始化为0

	for(int i=0; i<num; ++i){
		int neighbor_index = get_firstneighbor(get_vertex_symbol(i));
		while(neighbor_index != -1){
			int weight = edge[i][neighbor_index];
			if(early_start[i]+weight > early_start[neighbor_index]) //如果某点最早开始时间加上某点到其邻接点的权值大于该邻接点之前的权值,说明新路径长,更新该邻接点权值
				early_start[neighbor_index] = early_start[i]+weight;

			neighbor_index = get_nextneighbor(get_vertex_symbol(i),
										      get_vertex_symbol(neighbor_index));
		}
	}

	for(int i=0; i<num; ++i)
		cout << early_start[i] << "  ";   //打印最早开始时间
	cout << endl;

	for(int i=0; i<num; ++i)
		late_start[i] = MAX_COST;   //初始化为无穷时间
	late_start[num-1] = early_start[num-1];       //汇点的最早开始时间和最晚开始时间相同

	for(int i=num-2; i>=0; --i){    //除开汇点,从num-2开始
		int neighbor_index = get_firstneighbor(get_vertex_symbol(i));
		while(neighbor_index != -1){
			int weight = edge[i][neighbor_index];
			if(late_start[neighbor_index]-weight < late_start[i])//某点的邻接点的最晚开始时间减去权值小于某点之前的最晚开始时间,说明现有从后往前的路径长,则更新为最晚开始时间
				late_start[i] = late_start[neighbor_index]-weight;

			neighbor_index = get_nextneighbor(get_vertex_symbol(i),
				                              get_vertex_symbol(neighbor_index));
		}
	}

	for(int i=0; i<num; ++i)
		cout << late_start[i] << "  ";  //打印最晚开始时间
	cout << endl;

	for(int i=0; i<num; ++i)
		if(early_start[i] == late_start[i])
			cout << get_vertex_symbol(i) << "  ";  //若最早开始时间和最晚开始时间相等,则为关键路径

	delete []early_start;
	delete []late_start;
}

三:最短路径(迪杰斯特拉算法)

最短路径问题是图论研究中的一个经典算法问题, 旨在寻找图(由结点和路径组成的)中两结点之间的最短路径。

迪杰斯特拉算法用一个dist[]数组存储到达某点的花费,以其下标对应,并用一个path[]数组来存储到达该点前经过的某个顶点的下标。其中lable_incorporated[]数组用来标记已并入路径的顶点。

如图所示:

该图的最短路径为A D C E。

代码如下:

template <typename T, typename E>
void graph_mtx<T, E>::shortest_path(const T& vert)
{
	int num = this->get_numvertices();

	int *dist = new int[num];
	int *path = new int[num];
	int *lable_incorporated = new int[num];
	assert(dist != nullptr && path != nullptr
						   && lable_incorporated != nullptr);

	int index = get_vertex_index(vert);
	assert(index != -1);

	for(int i=0; i<num; ++i){
		lable_incorporated[i] = false;
		dist[i] = edge[index][i];
		if(edge[index][i] != 0 && dist[i] < MAX_COST)
			path[i] = index;   //如果从vert可以到达该点,路径默认为vert,表明到达该点的上一个顶点为vert
		else
			path[i] = -1;      //自身及不可达设为-1
	}
	lable_incorporated[index] = true;  //首先将起始点vert并入

	for(int i=0; i<num-1; ++i){
		int min = MAX_COST;
		int min_index = -1;

		for(int j=0; j<num; ++j){
			if(!lable_incorporated[j] && dist[j] < min){   //在未并入的顶点中找到最短可达的花费最小的顶点
				min = dist[j];
				min_index = j;
			}
		}
		lable_incorporated[min_index] = true;    //并入该顶点

		for(int j=0; j<num; ++j){
			int weight = edge[min_index][j];
			if(!lable_incorporated[j] && weight < MAX_COST //此处注意,如果不加weight<MAX_COST,那么dist[]数组重元素加上MAX_COST数据溢出,编译器默认为赋值,这就导致dist[min_index]+weight永远小于dist[j],出现错误
									  && dist[min_index]+weight < dist[j]){   //weight<MAX_COST
				dist[j] = dist[min_index]+weight;    //更新路径
				path[j] = min_index;
			}
		}
	}

	cout << "dist:" << " ";
	for(int i=0; i<num; ++i)
		cout << setw(3) << dist[i] << "  ";
	cout <<endl;

	cout << "path:" << " ";
	for(int i=0; i<num; ++i)
		cout << setw(3) << path[i] << "  ";

	delete []dist;
	delete []path;
}

四:全部代码及测试

通用图头文件:

#ifndef _GRAPH_H
#define _GRAPH_H

#include <iostream>
#include <string.h>
#include <assert.h>
#include <queue>
#include <iomanip>
using namespace::std;

#define MAX_COST 0x7FFFFFFF

///////////////////////////////////////////////////////////////////////////////////////////
//通用图结构
template <typename T, typename E>
class graph{
public:
	bool is_empty()const;
	bool is_full()const;

	int get_numvertices()const;    //当前顶点数
	int get_numedges()const;       //当前边数
public:
	virtual bool insert_vertex(const T&) = 0;            //插入顶点
	virtual bool insert_edge(const T&, const T&, E) = 0;    //插入边

	virtual int get_firstneighbor(const T&)const = 0;    //得到第一个邻接顶点
	virtual int get_nextneighbor(const T&, const T&)const = 0;    //某邻接顶点的下一个邻接顶点

	virtual void print_graph()const = 0;
	virtual int get_vertex_index(const T&)const = 0;     //得到顶点序号
protected:
	static const int VERTICES_DEFAULT_SIZE = 10;         //默认图顶点数
	int max_vertices;
	int num_vertices;
	int num_edges;
};

template <typename T, typename E>
bool graph<T, E>::is_empty()const
{
	return num_edges == 0;
}

template <typename T, typename E>
bool graph<T, E>::is_full()const
{
	return num_vertices >= max_vertices
		   || num_edges >= max_vertices*(max_vertices-1)/2;    //判满,分为顶点满和边满
}

template <typename T, typename E>
int graph<T, E>::get_numvertices()const
{
	return num_vertices;
}

template <typename T, typename E>
int graph<T, E>::get_numedges()const
{
	return num_edges;
}

///////////////////////////////////////////////////////////////////////////////////////////

#define VERTICES_DEFAULT_SIZE graph<T, E>::VERTICES_DEFAULT_SIZE
#define num_vertices          graph<T, E>::num_vertices
#define num_edges             graph<T, E>::num_edges
#define max_vertices          graph<T, E>::max_vertices         

///////////////////////////////////////////////////////////////////////////////////////////

#endif /*graph.h*/

实现文件:

#pragma once

#include "graph.h"

//图的邻接矩阵表示法
template <typename T, typename E>
class graph_mtx : public graph<T, E>{
public:
	graph_mtx(int);
	~graph_mtx();
public:
	bool insert_vertex(const T&);
	bool insert_edge(const T&, const T&, E);  

	int get_firstneighbor(const T&)const;
	int get_nextneighbor(const T&, const T&)const;

	int get_vertex_index(const T&)const;
	T& get_vertex_symbol(const int)const;
	void print_graph()const;

	void topological_sort();
	void shortest_path(const T&);
	void critical_path(const T&);
private:
	T* vertices_list;                        //顶点线性表
	E **edge;                              //内部矩阵
};

template <typename T, typename E>
graph_mtx<T, E>::graph_mtx(int sz = VERTICES_DEFAULT_SIZE)
{
	max_vertices = sz > VERTICES_DEFAULT_SIZE ? sz
									: VERTICES_DEFAULT_SIZE;
	vertices_list = new T[max_vertices];

	edge = new int*[max_vertices];                    //动态申请二维数组
	for(int i=0; i<max_vertices; ++i){
		edge[i] = new int[max_vertices];
	}

	for(int i=0; i<max_vertices; ++i)
		for(int j=0; j<max_vertices; ++j){
			if(i != j)
				edge[i][j] = MAX_COST;
			else
				edge[i][j] = 0;
		}

	num_vertices = 0;
	num_edges = 0;
}

template <typename T, typename E>
graph_mtx<T, E>::~graph_mtx()
{
	for(int i=0; i<max_vertices; ++i)
		delete []edge[i];                     //分别析构,再总析构

	delete edge;
	delete []vertices_list;
}

template <typename T, typename E>
bool graph_mtx<T, E>::insert_vertex(const T& vert)
{
	if(this->is_full())                       //派生类函数调用父类函数,用this或加作用域
		return false;
	vertices_list[num_vertices++] = vert;
	return true;
}

template <typename T, typename E>
bool graph_mtx<T, E>::insert_edge(const T& vert1, const T& vert2, E cost = MAX_COST)
{
	if(this->is_full())                       //判满
		return false;

	int index_v1 = get_vertex_index(vert1);   //得到顶点序号
	int index_v2 = get_vertex_index(vert2);

	if(index_v1 == -1 || index_v2 == -1 )
		return false;

	edge[index_v1][index_v2] = cost;    //无向图
	++num_edges;	

	return true;
}

template <typename T, typename E>
int graph_mtx<T, E>::get_firstneighbor(const T& vert)const
{
	int index = get_vertex_index(vert);

	if(index != -1){
		for(int i=0; i<num_vertices; ++i){
			if(edge[index][i] != 0 && edge[index][i] != MAX_COST)
				return i;
		}
	}
	return -1;
}

template <typename T, typename E>
int graph_mtx<T, E>::get_nextneighbor(const T& vert1, const T& vert2)const
{
	int index_v1 = get_vertex_index(vert1);
	int index_v2 = get_vertex_index(vert2);

	if(index_v1 != -1 && index_v2 != -1){
		for(int i=index_v2+1; i<num_vertices; ++i){
			if(edge[index_v1][i] != 0 && edge[index_v1][i] != MAX_COST)
				return i;
		}
	}
	return -1;
}

template <typename T, typename E>
int graph_mtx<T, E>::get_vertex_index(const T& vert)const
{
	for(int i=0; i<num_vertices; ++i){
		if(vertices_list[i] == vert)
			return i;
	}
	return -1;
}

template <typename T, typename E>
T& graph_mtx<T, E>::get_vertex_symbol(const int index)const
{
	assert(index >= 0 && index < this->get_numvertices());
	return vertices_list[index];
}

template <typename T, typename E>
void graph_mtx<T, E>::print_graph()const
{
	if(this->is_empty()){
		cout << "nil graph" << endl;                      //空图输出nil
		return;
	}

	for(int i=0; i<num_vertices; ++i){
		cout << vertices_list[i] << "  ";
	}
	cout << endl;

	for(int i=0; i<num_vertices; ++i){
		for(int j=0; j<num_vertices; ++j){
			if(edge[i][j] != MAX_COST)
				cout << edge[i][j] << "  ";
			else
				cout << '@' << "  ";
		}
		cout << vertices_list[i] << endl;
	}
}

template <typename T, typename E>
void graph_mtx<T, E>::topological_sort()
{
	int num = this->get_numvertices();
	assert(num != -1);

	int *count = new int[num];
	assert(count != nullptr);

	for(int i=0; i<num; ++i)
		for(int j=0; j<num; ++j){
			if(edge[i][j] != 0 && edge[i][j] != MAX_COST)
				count[j]++;
		}

	int top = -1;
	for(int i=0; i<num; ++i)
		if(count[i] == 0){
			count[i] = top;
			top = i;
		}

	for(int i=0; i<num; ++i){
		if(top == -1)
			return ;

		int index_tmp = top;
		top = count[top];

		cout << get_vertex_symbol(index_tmp) << "  ";

		int neighbor_index = get_firstneighbor(get_vertex_symbol(index_tmp));
		while(neighbor_index != -1){
			if(--count[neighbor_index] == 0){
				count[neighbor_index] = top;
				top = neighbor_index;
			}
			neighbor_index = get_nextneighbor(get_vertex_symbol(index_tmp),
											  get_vertex_symbol(neighbor_index));
		}
	}

	delete []count;
}

template <typename T, typename E>
void graph_mtx<T, E>::shortest_path(const T& vert)
{
	int num = this->get_numvertices();

	int *dist = new int[num];
	int *path = new int[num];
	int *lable_incorporated = new int[num];
	assert(dist != nullptr && path != nullptr
						   && lable_incorporated != nullptr);

	int index = get_vertex_index(vert);
	assert(index != -1);

	for(int i=0; i<num; ++i){
		lable_incorporated[i] = false;
		dist[i] = edge[index][i];
		if(edge[index][i] != 0 && dist[i] < MAX_COST)
			path[i] = index;
		else
			path[i] = -1;
	}
	lable_incorporated[index] = true;

	for(int i=0; i<num-1; ++i){
		int min = MAX_COST;
		int min_index = -1;

		for(int j=0; j<num; ++j){
			if(!lable_incorporated[j] && dist[j] < min){
				min = dist[j];
				min_index = j;
			}
		}
		lable_incorporated[min_index] = true;

		for(int j=0; j<num; ++j){
			int weight = edge[min_index][j];
			if(!lable_incorporated[j] && weight < MAX_COST
									  && dist[min_index]+weight < dist[j]){   //weight<MAX_COST
				dist[j] = dist[min_index]+weight;
				path[j] = min_index;
			}
		}
	}

	cout << "dist:" << " ";
	for(int i=0; i<num; ++i)
		cout << setw(3) << dist[i] << "  ";
	cout <<endl;

	cout << "path:" << " ";
	for(int i=0; i<num; ++i)
		cout << setw(3) << path[i] << "  ";

	delete []dist;
	delete []path;
}

template <typename T, typename E>
void graph_mtx<T, E>::critical_path(const T& vert)
{
	int num = this->get_numvertices();

	int *early_start = new int[num];
	int *late_start = new int[num];
	assert(early_start != nullptr && late_start != nullptr);

	int index = get_vertex_index(vert);
	assert(index != -1);

	for(int i=0; i<num; ++i)
		early_start[i] = 0;

	for(int i=0; i<num; ++i){
		int neighbor_index = get_firstneighbor(get_vertex_symbol(i));
		while(neighbor_index != -1){
			int weight = edge[i][neighbor_index];
			if(early_start[i]+weight > early_start[neighbor_index])
				early_start[neighbor_index] = early_start[i]+weight;

			neighbor_index = get_nextneighbor(get_vertex_symbol(i),
										      get_vertex_symbol(neighbor_index));
		}
	}

	for(int i=0; i<num; ++i)
		cout << early_start[i] << "  ";
	cout << endl;

	for(int i=0; i<num; ++i)
		late_start[i] = MAX_COST;
	late_start[num-1] = early_start[num-1];

	for(int i=num-2; i>=0; --i){
		int neighbor_index = get_firstneighbor(get_vertex_symbol(i));
		while(neighbor_index != -1){
			int weight = edge[i][neighbor_index];
			if(late_start[neighbor_index]-weight < late_start[i])
				late_start[i] = late_start[neighbor_index]-weight;

			neighbor_index = get_nextneighbor(get_vertex_symbol(i),
				                              get_vertex_symbol(neighbor_index));
		}
	}

	for(int i=0; i<num; ++i)
		cout << late_start[i] << "  ";
	cout << endl;

	for(int i=0; i<num; ++i)
		if(early_start[i] == late_start[i])
			cout << get_vertex_symbol(i) << "  ";

	delete []early_start;
	delete []late_start;
}

测试文件:

#include "graph.h"
#include "graph_mtx.h"

int main()
{
	graph_mtx<char, int> gm;

//critical_path
	gm.insert_vertex('A');
	gm.insert_vertex('B');
	gm.insert_vertex('C');
	gm.insert_vertex('D');
	gm.insert_vertex('E');
	gm.insert_vertex('F');
	gm.insert_vertex('G');
	gm.insert_vertex('H');
	gm.insert_vertex('I');

	gm.insert_edge('A', 'B', 6);
	gm.insert_edge('A', 'C', 4);
	gm.insert_edge('A', 'D', 5);
	gm.insert_edge('B', 'E', 1);
	gm.insert_edge('C', 'E', 1);
	gm.insert_edge('D', 'F', 2);
	gm.insert_edge('E', 'G', 9);
	gm.insert_edge('E', 'H', 7);
	gm.insert_edge('G', 'I', 2);
	gm.insert_edge('H', 'I', 5);
	gm.insert_edge('F', 'H', 4);

	gm.critical_path('A');
	cout << endl;
#if 0
//shortest_path
	gm.insert_vertex('A');
	gm.insert_vertex('B');
	gm.insert_vertex('C');
	gm.insert_vertex('D');
	gm.insert_vertex('E');

	gm.insert_edge('A', 'B', 10);
	gm.insert_edge('A', 'D', 30);
	gm.insert_edge('A', 'E', 100);
	gm.insert_edge('B', 'C', 50);
	gm.insert_edge('C', 'E', 10);
	gm.insert_edge('D', 'C', 20);
	gm.insert_edge('D', 'E', 60);

	cout << "shortest_path:" << endl;
	gm.shortest_path('A');
	cout << endl;
#endif
#if 0
//topological_sort
	gm.insert_vertex('A');
	gm.insert_vertex('B');
	gm.insert_vertex('C');
	gm.insert_vertex('D');
	gm.insert_vertex('E');
	gm.insert_vertex('F');

	gm.insert_edge('A', 'B', 6);
	gm.insert_edge('A', 'C', 1);
	gm.insert_edge('A', 'D', 5);
	gm.insert_edge('C', 'B', 5);
	gm.insert_edge('C', 'E', 3);
	gm.insert_edge('D', 'E', 5);
	gm.insert_edge('F', 'E', 4);
	gm.insert_edge('F', 'D', 2);

	cout << "topological_sort:" << endl;
	gm.topological_sort();
	cout << endl;

#endif

	return 0;
}

部分测试结果:

时间: 2024-08-07 11:59:21

图的拓扑排序、关键路径、最短路径算法 -- C++实现的相关文章

【数据结构】拓扑排序、最短路径算法、Dijkstra算法、无环图等等

图的定义 图(graph)G = (V,E)由顶点(vertex)的集V和边(Edge)的集E组成.有时也把边称作弧(arc),如果点对(v,w)是有序的,那么图就叫做有向的图(有向图).顶点v和w邻接(adjacent)当且仅当(v,w)属于E. 如果无向图中从每一个顶点到其他每个顶点都存在一条路径,则称该无向图是连通的(connected).具有这样性质的有向图称为是强连通的(strongly connected).如果有向图不是强连通的,但它的基础图(underlying graph)(也

图的拓扑排序——卡恩算法

拓扑排序 有向图的拓扑排序是其顶点的线性排序,使得对于从顶点u 到顶点v 的每个有向边uv ,u 在排序中都在v 之前. 在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(Topological sorting). 每个顶点出现且只出现一次: 若A在序列中排在B的前面,则在图中不存在从B到A的路径. //#include<Windows.h> #include<iostream> #include<cstring> #inclu

图的拓扑排序(转)

一.概述 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前.   通常,这样的线性序列称为满足拓扑次序(TopoiSicai Order)的序列,简称拓扑序列.注意:   ①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的.   ②若图中存在有向环,则不可能使顶点满足拓扑次序.   ③一个DAG的拓扑序列通常表示某种方案

有向无环图(DAG)拓扑排序的两种方法

如下图的DAG: 第一种: (1)从AOV网中选择一个没有前驱的顶点并且输出它: (2)从AOV网中删除该顶点,并且上去所有该顶点为尾的弧: (3)重复上述两步,直到全部顶点都被输出,或者AOV网中不存在没有前驱的顶点. 第二种: 使用深度优先搜索(DFS),并标记每一个节点的第一次访问(pre)和最后一次访问时间(post),最后post的逆序就是DAG的拓扑排序,其实也是节点在进行DFS搜索时,出栈的逆序就是拓扑排序. 拓扑序列的结果有: (1) c++,高等数学,离散数学,数据结构,概率论

图的拓扑排序

定义: 图的拓扑排序是对有向无环图来说的,无向图和有环图没有拓扑排序,或者说不存在拓扑排序,对于一个有向无环图G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若图G存在边< u, v >,则u在线性序列中出现在v之前.对一个有向无环图进行拓扑排序产生的线性序列称为满足拓扑次序的序列.一个有向无环图可以表示某种动作或者方案,或者状态,而有向无环图的拓扑序列通常表示某种某案切实可行或者各个成员之间的某种关系. 举个栗子,看上面那个图,v1v2v3v4v5v6v7v8是

java实现AOV图的拓扑排序

拓扑排序作为图的应用,了解拓扑排序必须首先了解AOV图. AOV网表示一个有向图中顶点,用弧表示顶点之间的优先关系.如下图所示,在AOV网中,若从顶点vi到顶点vj之间存在一条有向路径,则称顶点vi为顶点vj的前驱,顶点vj为顶点vi的后继.注意,AOV图不能有回路,否则会将序列陷入死循环,称为死锁. 进行拓扑排序,一般步骤如下: <1>在AOV网中选择一个入度为0的顶点 <2>在AOV网中删除此顶点及其相连的有向边 <3>重复步骤<1>和<2>

DAG图的拓扑排序 python

在DAG中DFS中顶点的出栈顺序即逆拓扑序. def topological_sort( graph ): is_visit = dict( ( node, False ) for node in graph ) li = [] def dfs( graph, start_node ): for end_node in graph[start_node]: if not is_visit[end_node]: is_visit[end_node] = True dfs( graph, end_n

拓扑排序((算法竞赛入门经典)刘汝佳)

转载请注明出处:http://blog.csdn.net/u012860063? viewmode=contents [分析](小白) 把每一个变量看成一个点,"小于"关系看成有向边,则我们得到了一个有向图.这样,我们的任务实际上是把一个图的全部结点排序,使得每一条有向边(u,v)相应的u都排在v的前面.在图论中,这个问题称为拓扑排序. 不难发现:假设图中存在有向环,则不存在拓扑排序,反之则存在.我们把不包括有向环的有向图称为有向无环图.能够借助dfs函数完毕拓扑排序:在訪问完一个结点

算法学习 - 图的拓扑排序

拓扑排序 拓扑排序是对有向无圈图的顶点的一种排序,使得如果存在一条从Vi到Vj的路径,那么排序中Vj一定出现在Vi后面. 所以假如图里面有圈就不可能完成排序的. 第一种方法 一种简单的办法就是在排序算法中,先找到任意一个没有入边的顶点,然后显示该顶点,并把它和它的边一起从图里删掉.依次类推到最后. 入度(indegree): 顶点v的入度为,所有指向顶点v的变数(u, v). 出度(outdegree): 顶点v的出度为,顶点v所发出的边数(v, u). 下面写下这种方法的伪代码,因为这个的时间