拓扑排序

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

       一个最简单的例子就是我们的学习过程,我们的学习顺序是幼儿园-小学-中学-大学。当然我们在阅读一本书的时候也是这个样子的,特别是专业书,有时我们需要其他几本书的基础上才能读懂这本书。这就是先后后顺序,也是拓扑排序的主要思想。

       在图论中,能够进行拓扑排序的是有向无环图。这样的图才能够进行排序,因为每一个顶点出发后都不会回到自身,这样就满足了事情发展的顺序。也就是说我们不可能读完大学后在回去读幼儿园。

       为了形式化,我们需要求出每一个顶点的入度(indegree),用数组来储存结果(ex:下图顶点4的入度为3,indegree[4]=3),求出入度后就需要一个队列或者栈来存放入度为0的顶点,下面我们使用队列来讲解。首先我们找出入度为0的顶点,入队。接着出队,出的的顶点v指向的顶点(w0,w1,...)的入度减一,并检查这些顶点(w0,w1,...)的入度是否为零,是的话入队。接着出队,重复以上操作,知道队列为空。

下图拓扑顺序为:1->2->5->4->3->7->6




                                       

一.邻接表的拓扑 

               
                     
#include<iostream>
#include<queue>
using namespace std;

const int NumVertex = 7;

typedef struct Node {
	int index;
	struct Node *next;
}Node;

typedef struct VertexNode {
	int date;
	Node *firstarc;
}VertexNode;

typedef struct AGraph {
	VertexNode adjlist[NumVertex+1];
	int NumVertexs, NumEdges;
}AGraph;

void InPutEdge(AGraph &G, int v, int w) {//输入v指向w的边
	Node *p;
	if (!(p = (Node *)malloc(sizeof(Node))))
		cout << "FAILED MALLOC!" << endl;
	p->index = w;
	p->next = G.adjlist[v].firstarc;
	G.adjlist[v].firstarc = p;
}

void Create(AGraph &G) {
	int i;
	G.NumVertexs = NumVertex;
	G.NumEdges = 12;
	//初始化邻接表
	for (i = 1; i <= G.NumVertexs; ++i) {
		G.adjlist[i].date = i;
		G.adjlist[i].firstarc = NULL;
	}
	InPutEdge(G, 1, 2);
	InPutEdge(G, 1, 4); 
	InPutEdge(G, 1, 3); 
	InPutEdge(G, 2, 5); 
	InPutEdge(G, 2, 4);
	InPutEdge(G, 3, 6);
	InPutEdge(G, 4, 3);
	InPutEdge(G, 4, 6);
	InPutEdge(G, 4, 7);
	InPutEdge(G, 5, 4);
	InPutEdge(G, 5, 7);
	InPutEdge(G, 7, 6);
}

void Set_Indegree(const AGraph &G,int indegree[]) {
	int i;
	//初始化indefree数组
	for (i = 0; i < G.NumVertexs; indegree[++i] = 0);
	Node *p;
	for (i = 1; i <= G.NumVertexs; ++i) {
		p = G.adjlist[i].firstarc;
		while (p) {
			++indegree[p->index];
			p = p->next;
		}
	}
}

void OutPutGraph(const AGraph &G) {//输出整个图
	Node *p;
	for (int i = 1; i <= G.NumVertexs; ++i) {
		p = G.adjlist[i].firstarc;
		if (!p)
			cout << "Vertex " << i << " point to none!" << endl;
		else {
			while (p) {
				cout << i << " -> " << p->index << endl;
				p = p->next;
			}
		}
	}
}

/*拓扑排序*/
void TopoSort(const AGraph &G) {
	cout << "TopoSort:" << endl;
	queue<int> Q;
	int i, v;
	int counter = 0;
	Node *p;
	int indegree[NumVertex + 1];//用于存放各顶点的入度
	Set_Indegree(G, indegree);
	for (i = 1; i <= G.NumVertexs; ++i)//检查入度,入度为0的顶点入队
		if (indegree[i] == 0) {
			Q.push(i);
			--indegree[i];
		}
	while (!Q.empty()) {
		v = Q.front();
		Q.pop();
		++counter;
		cout << v << " ";
		//将v所指向的所有顶点的入度减一
		p = G.adjlist[v].firstarc;
		while (p) {
			//检查各顶点入度,为0入队
			if (--indegree[p->index] == 0) {
				--indegree[p->index];
				Q.push(p->index);
			}
			p = p->next;//指向顶点的下一条边
		}
	}
	if (counter < G.NumVertexs)
		cout << "Graph has a cycle!" << endl;
}

void main() {
	AGraph G;
	Create(G);
	OutPutGraph(G);
	TopoSort(G);
	system("pause");
}

二.邻接矩阵的拓扑排序


#include<iostream>  
#include<queue>
using namespace std;

const int MAX_NUM = 100;
const int INF = 65536;

template<typename node_name>
class MGraph {
public:
	//初始化
	MGraph(int vertexs, int edge) :_vertexs(vertexs),
		                            _Edges(edge) 
	{
		for (int i = 0; i < _vertexs; ++i)
			for (int j = 0; j < _vertexs; ++j)
				arcs[i][j] = INF;
		for (int i = 0; i < _vertexs; ++i)
			vertex[i] = i + 1;
	}
	void Create() {//创建图
		Set_Arc(1, 2);
		Set_Arc(1, 3); 
		Set_Arc(1, 4);
		Set_Arc(2, 4);
		Set_Arc(2, 5);
		Set_Arc(3, 6);
		Set_Arc(4, 6);
		Set_Arc(4, 7);
		Set_Arc(4, 3);
		Set_Arc(5, 7);
		Set_Arc(5, 4);
		Set_Arc(7, 6);
	}
	void Print_Edge() {//输出每一条边
		for (int i = 0; i < _vertexs; ++i)
			for (int j = 0; j < _vertexs; ++j)
				if (arcs[i][j] == 1)
					cout << vertex[i] << "->" << vertex[j] <<  endl;
	}
	void TopoSort() {
		int i, tmp;
		queue<int> Q;
		int Indegree[7];
		for (i = -1; i < _vertexs - 1; Indegree[++i] = 0);
		Set_Indegree(Indegree);
		for (i = 0; i < _vertexs; ++i)//检查各点入度,为0入队
			if (Indegree[i] == 0) {
				Q.push(i);
				--Indegree[i];//减一保证这个顶点下次检查时不会被入队
			}
		while (!Q.empty()) {
			tmp = Q.front();
			Q.pop();
			cout << vertex[tmp] << " ";
			for (i = 0; i < _vertexs; ++i)//将出队的顶点指向的顶点的入度减一
				if (arcs[tmp][i] == 1)
					--Indegree[i];
			for (i = 0; i < _vertexs; ++i)//检查入度,同上
				if (Indegree[i] == 0) {
					Q.push(i);
					--Indegree[i];
				}
		}
		cout << endl;
	}
private:
	void Set_Arc(int a, int b) {//设置a指向b的边
		arcs[a-1][b-1] = 1;
	}
	void Set_Indegree(int *indegree) {//生成入度
		for (int i = 0; i < _vertexs; ++i)
			for (int j = 0; j < _vertexs; ++j)
				if (arcs[i][j] == 1)
					++indegree[j];
	}
private:
	int _vertexs;//节点数
	int _Edges;//边数
	node_name vertex[MAX_NUM];//顶点信息
	float arcs[MAX_NUM][MAX_NUM];//矩阵,用于存放权值

};

void main() {
	MGraph<int> G(7, 12);
	G.Create();
	G.Print_Edge();
	G.TopoSort();
	system("pause");
}





               

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值