拓扑排序

基本概念:

      拓扑排序是对有向无圈图的顶点的一种排序,若一条路径是从vi到vj,那么在排序序列中vj出现在vi后面。显然,如果图含有圈,那么拓扑排序是不可能的,比如对于圈上的两个顶点v和w,v在w之前同时w又在v之前,在排序中是矛盾的。另外,拓扑排序不是唯一的,比如下面一张图的拓扑排序可以是v1,v2,v5,v4,v3,v7,v6或者v1,v2,v5,v4,v7,v3,v6。


算法设计:

1.简单的拓扑排序

      基本思路:找出任意一个没有入边的顶点,然后显示该顶点,并将它和它的边从图中一起删除。然后对图的其余部分应用同样的方法。
      具体细节实现:
      ①把顶点v的入度定义为边(u,v)的条数。
      ②计算图中所有顶点的入度,保存在数组Indegree中。
      ③图被读入一个邻接表中。
      下面是代码实现,测试数据是上图中的顶点:
#include <iostream>
#include <vector>
#include <cstdlib>

using namespace std;

const int MaxSize = 20;

struct ArcNode{
	int adjvex;
	ArcNode* next;
};

//头单元数组
template<class Type>
struct VertexNode{
	Type vertex;
	ArcNode* firstedge;
};

template<class Type>
class Graph{
private:
	VertexNode<Type> adjlist[MaxSize];
	int vertexNum, arcNum;
	Type vertex[MaxSize];
	int Indegree[MaxSize];
public:
	Graph(){
		Type v;
		int i, j;
		vertexNum = 0;
		arcNum = 0;
		cout << "please input the nodes:" << endl;
		while (cin >> v){
			adjlist[vertexNum].vertex = v;
			adjlist[vertexNum].firstedge = NULL;
			vertex[vertexNum] = v;
			Indegree[vertexNum] = 0;
			vertexNum++;
		}
		cin.clear();
		cout << "please input the edges:" << endl;
		Type a, b;
		while (cin >> a >> b){
			findNode(a, b, i, j);
			Indegree[j]++;//计算各顶点的入度
			ArcNode* s = new ArcNode;
			s->adjvex = j;
			s->next = adjlist[i].firstedge;
			adjlist[i].firstedge = s;
			arcNum++;
		}
		cin.clear();
	}

	void findNode(Type a, Type b, int& i, int& j){
		for (int k = 0; k < vertexNum; k++){
			if (vertex[k] == a)
				i = k;
			if (vertex[k] == b)
				j = k;
		}
	}

	void TopSort(){
		int counter;
		Type V, W;
		int Index;
		for (counter = 0; counter < vertexNum; counter++){
			Index = FindNewVertexOfIndegreeZero();
			if (Index == -1){
				cout << "Graph has a cycle!" << endl;
				exit(1);
			}
			cout << vertex[Index] << " "<<flush;
			for (ArcNode* p = adjlist[Index].firstedge; p != NULL; p = p->next){
				Indegree[p->adjvex]--;
			}
		}
	}

	int FindNewVertexOfIndegreeZero(){
		for (int i = 0; i < vertexNum; i++){
			if (Indegree[i] == 0){
				Indegree[i] = -1;//取出该顶点,下次不再考虑
				return i;
			}	
		}
		return -1;
	}
};

int main(){
	Graph<char> g;
	g.TopSort();
	cout << endl;
	return 0;
}
       效率分析:函数FindNewVertexOfIndegreeZero扫描Indegree数组,寻找一个尚未被分配拓扑编号的入度是0的顶点。如果返回-1则表示这样的顶点不存在,也就意味着该图有圈。因为FindNewVertexOfIndegreeZero函数是对Indegree数组的简单顺序扫描,若以每次要花费O(|V|)的时间。由于有|V|次的这样的调用,所以该算法的运行时间为O(V^2)。
      测试结果:


2.改进的拓扑排序

       上述算法运行时间长的原因在于对Indegree数组的顺序扫描。如果图是稀疏的,那么在每次迭代期间只有一些顶点的入度被更新,但是,尽管只有一小部分发生了变化,但是在搜索入度为0的顶点时潜在地查看了所有的顶点。
      解决方法:使用一个队列,首先,对每一个顶点计算入度,将所有入度为0的顶点放入一个空的队列中。当队列不空时,删除一个顶点v,并将和v邻接的顶点的入度减1,只要有一个顶点的入度降为0,就把该顶点放入队列中。此时,拓扑排序就是顶点出队的顺序。
      下面是代码实现,测试数据 同上:
#include <iostream>
#include <queue>
#include <cstdlib>

using namespace std;

const int MaxSize = 20;
struct ArcNode
{
	int adjvex;
	ArcNode* next;
};

template<class Type>
struct VertexNode{
	Type vertex;
	ArcNode* firstedge;
};

template<class Type>
class Graph{
private:
	VertexNode<Type> adjlist[MaxSize];
	int vertexNum, arcNum;
	Type vertex[MaxSize];
	int Indegree[MaxSize];
public:
	Graph(){
		Type v;
		int i, j;
		vertexNum = 0;
		arcNum = 0;
		cout << "please input the nodes:" << endl;
		while (cin >> v){
			adjlist[vertexNum].vertex = v;
			adjlist[vertexNum].firstedge = NULL;
			vertex[vertexNum] = v;
			Indegree[vertexNum] = 0;
			vertexNum++;
		}
		cin.clear();
		cout << "please input the edges:" << endl;
		Type a, b;
		while (cin >> a >> b){
			findNode(a, b, i, j);
			Indegree[j]++;//计算各顶点的入度
			ArcNode* s = new ArcNode;
			s->adjvex = j;
			s->next = adjlist[i].firstedge;
			adjlist[i].firstedge = s;
			arcNum++;
		}
		cin.clear();
	}

	void findNode(Type a, Type b, int& i, int& j){
		for (int k = 0; k < vertexNum; k++){
			if (vertex[k] == a)
				i = k;
			if (vertex[k] == b)
				j = k;
		}
	}

	int findNode(Type a){
		for (int k = 0; k < vertexNum; k++){
			if (vertex[k] == a)
				return k;
		}
	}

	void TopSort(){
		queue<Type> q;
		int count = 0;
		int Index;
		int i;
		for (i = 0; i < vertexNum; i++){
			if (Indegree[i] == 0)
				q.push(vertex[i]);
		}
		while (!q.empty()){
			Type v = q.front();
			cout << v << " " << flush;
			q.pop();
			count++;
			Index = findNode(v);
			for (ArcNode* p = adjlist[Index].firstedge; p != NULL; p = p->next){
				if (--Indegree[p->adjvex]==0){
					q.push(vertex[p->adjvex]);
				}
			}
		}
		if (count != vertexNum){
			cout << "Graph has a cycle!" << endl;
			exit(1);
		}
	}
};

int main(){
	Graph<char> g;
	g.TopSort();
	cout << endl;
	return 0;
}
      效率分析:如果使用邻接表,这个算法的运行时间为O(|E|+|V|),因为for循环对每条边最多执行一次,队列操作对每个顶点也是最多进行一次。
      测试结果:



      
      ps:整理自《数据结构与算法分析——C语言描述》
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值