图的拓扑排序

1. 相关概念

1.1 AOV网

用一个有向图表示一个工程的各子工程及其相互制约的关系,其中以顶点表示活动弧表示活动之间的优先制约关系,称这种有向图为顶点表示活动的网,简称AOV网(Activity On Vertex network)。

1.2 AOV网的特点

若从i到j有一条有向路径,则i是j的前驱,j是i的后继。
若<i,j>是网中有向边,则i是j的直接前驱;j是i的直接后继。
AOV网中不允许有回路,因为如果有回路存在,则表明某项活动以自己为先决条件,显然这是荒谬的。

2. 拓扑排序

2.1 概念

在AOV网没有回路的前提下,我们将全部活动排列成一个线性序列,使得若AOV网中有弧<i,j>存在,则在这个序列中,i一定排在j的前面,具有这种性质的线性序列称为拓扑有序序列,相应的拓扑有序排序算法称为拓扑排序

2.2 拓扑排序方法

在有向图中选一个没有前驱的顶点且输出之;
从图中删除该顶点和所有以它为尾的弧;
重复上述两步,直至全部顶点均已输出,或者当图中不存在无前驱的顶点为止。

算法复杂度:O(n+e)

注:由于每次选择的无前驱顶点可能不同,所以一个AOV网的拓扑序列不是唯一的

2.3 拓扑排序的应用

检测图中是否存在环
对有向图构造其顶点的拓扑有序序列,若图中所有顶点都在它的拓扑有序序列中,则该图中必定不存在环。

3. 代码

#include <iostream>
using namespace std;

/**************************************************************************************
******************************** 图的邻接表结构定义及操作 ********************************
***************************************************************************************/
typedef char VertexType;	// 顶点类型,自定义成char
typedef int  WeightType;	// 边上的权值类型,自定义成int

#define MAXVEX	 (100)		// 最大顶点数

/* 边表结点 */
typedef struct EdgeNode {
	int			adjvex;		// 邻接点域,存储该顶点对应的下标
	// WeightType	weight;		// 权值域,存储顶点权值,非网图(无权图)不必定义
	EdgeNode* next;			// 链域,指向下一个邻接点
} EdgeNode;

/* 顶点结点 */
typedef struct VertexNode {
	int in;						// 顶点入度
	VertexType	data;			// 顶点域,存储顶点值
	EdgeNode* firstEdge;		// 边表头指针,指向第一个邻接点
} VertexNode, AdjList[MAXVEX];	// AdjList表示邻接表类型

typedef struct {
	AdjList adjList;		// 邻接表,相当于:VertexNode adjList[MAXVEX];
	int Nv, Ne;				// 图中顶点数和边数
} ALGraph;

/* 建立图的邻接表结构 */
void createALGraph(ALGraph* G) {
	cout << "输入顶点数和边数:";
	cin >> G->Nv >> G->Ne;

	// 读入顶点信息
	for (int i = 0; i < G->Nv; i++)
	{
		cout << "输入顶点数据及其入度:";
		cin >> G->adjList[i].data;			// 读入顶点数值
		cin >> G->adjList[i].in;			// 读入顶点入度
		G->adjList[i].firstEdge = nullptr;	// 将边表指针置为空
	}

	// 读入Ne条边,建立邻接表
	int i, j;
	// WeightType w;
	EdgeNode* e;
	for (int k = 0; k < G->Ne; k++)
	{
		/*
		cout << "输入边(vi, vj)的下标及i、j及权值w:" << endl;
		cin >> i >> j >> w;
		*/
		cout << "输入边(vi, vj)的下标及i、j:";
		cin >> i >> j;

		e = new EdgeNode;
		e->adjvex = j;						// 邻接点下标为j
		// e->weight = w;						// 边(vi, vj)上的权值
		e->next = G->adjList[i].firstEdge;	// 将e的next指针指向当前顶点firstEdge指向的结点
		G->adjList[i].firstEdge = e;		// 将当前顶点的firstEdge指针指向e
		/* 上面两行:使用头插法将结点插入链表中 */

		// 以下为无向图需要部分,有向图不需要 
		/*e = new EdgeNode;
		e->adjvex = i;
		e->weight = w;
		e->next = G->adjList[j].firstEdge;
		G->adjList[j].firstEdge = e;*/
	}
}

/* 销毁邻接表 */
void destroyAdjList(ALGraph* G) {
	for (int i = 0; i < G->Nv; i++)
	{
		while (G->adjList[i].firstEdge)
		{
			EdgeNode* e = G->adjList[i].firstEdge;
			G->adjList[i].firstEdge = e->next;
			delete e;
		}
	}
}

/**************************************************************************************
*************************************** 拓扑排序 ***************************************
***************************************************************************************/
/**
* @brief:		拓扑排序,若G无回路,则输出拓扑排序序列并返回0,若有回路返回-1
* @param GL:	邻接表表示的图指针
* @return:		0 —— 成功
*				-1 —— 失败
*/
int TopologicalSort(ALGraph* G)
{
	EdgeNode* e;
	int i, k, gettop;
	int top = -1;	// 用于栈指针下标
	int count = 0;	// 用于统计输出顶点的个数
	int* stack;		// 建栈存储入度为0的顶点
	stack = new int[G->Nv];

	for (i = 0; i < G->Nv; i++)
	{
		if (G->adjList[i].in == 0)
		{
			stack[++top] = i;	// 将入度为0的顶点入栈
		}
	}

	while (top != -1)
	{
		gettop = stack[top--];	// 出栈
		cout << G->adjList[gettop].data << "\t";
		count++;

		/* 遍历该顶点对应的边表 */
		for (e = G->adjList[gettop].firstEdge; e; e = e->next)
		{
			k = e->adjvex;
			G->adjList[k].in--;			// 将k号顶点的入度减1
			if (G->adjList[k].in == 0)	// 若入度为0,入栈
			{
				stack[++top] = k;
			}
		}

	}

	if (count < G->Nv)		// 如果count小于顶点数,说明存在环
	{
		return -1;
	}
	else
	{
		return 0;
	}
}

int main()
{
	ALGraph* G = new ALGraph;
	// 建图
	createALGraph(G);

	// 拓扑排序
	TopologicalSort(G);

	// 销毁邻接表
	destroyAdjList(G);

	delete G;
}

运行结果:
以《大话数据结构》图7-8-2为例。
在这里插入图片描述

4. 参考书籍

大话数据结构-程杰
青岛大学数据结构-王卓

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MinBadGuy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值