拓扑排序

在介绍拓扑排序前我们需要了解什么是偏序和全序。

1、偏序——若集合X上的关系R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。

2、全序——设R是集合X上的偏序,如果对每个x,y∈X必有xRy或yRx,则称R是集合X上的全序关系。

而由某个集合上的一个偏序得到该集合上的一个全序,这个操作就称之为拓扑排序。

拓扑排序的经典例子就是选课问题,数据结构书籍上都有介绍,这里不再赘述。那么如何求得一个有向图的拓扑序列呢?对一个有向图,如果图中不存在环,则可以通过以下三步完成拓扑排序:

1、从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它。

2、从网中删去该顶点,并且删去从该顶点发出的全部有向边。

3、重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止。

对于图一中的有向图,由以上方法进行拓扑排序如下:


采用邻接矩阵存储有向图的拓扑排序具体实现如下:

#include <cstdio>
#include <cstdlib>

#define VERTEXNUM 100 //存储顶点数目
typedef char VertexType;
typedef int EdgeType;
typedef int InDegreeType;
/**********************************************
 *
 * 邻接矩阵存储结构
 *
 **********************************************/
typedef struct
{
	VertexType vertexs[VERTEXNUM]; //顶点表
	InDegreeType indegree[VERTEXNUM]; //入度
	EdgeType edges[VERTEXNUM][VERTEXNUM]; //邻接矩阵存储顶点间关系
	int vernum, edgenum; //图中当前的顶点和边数
}Graph;

int topsort[VERTEXNUM]; //保存拓扑排序后顶点序号

/**********************************************
 *
 * 建立邻接矩阵
 *
 **********************************************/
void MakeGraph(Graph *&graph)
{
	int i, j, k;
	printf("请输入图的顶点数n和边数e:\n");
	scanf("%d%d", &graph->vernum, &graph->edgenum);
	printf("请输入顶点信息(顶点号<CR>)每个顶点以回车作为结束:\n");
	for(i = 0; i < graph->vernum; i++)
	{
		getchar();
		scanf("%c", &graph->vertexs[i]);
	}

	for(i = 0; i < graph->vernum; i++) //边信息初始化
	{
		for(j = 0; j < graph->vernum; j++)
		{
			graph->edges[i][j] = 0;
		}
	}

	printf("请输入每条边对应的两个顶点的序号(格式为i,j):\n");
	for(k = 0; k < graph->edgenum; k++)
	{
		scanf("%d,%d", &i, &j);
		
		graph->edges[i - 1][j - 1] = 1;
	}
}

/**********************************************
 *
 * 计算图中各顶点的入度
 *
 **********************************************/
void GetInDegree(Graph *graph)
{
	int i, j;
	for(i = 0; i < graph->vernum; i++)
		graph->indegree[i] = 0;

	for(i = 0; i < graph->vernum; i++) //计算图中各顶点的入度
	{
		for(j = 0; j < graph->vernum; j++)
		{
			if(graph->edges[i][j] == 1)
			{
				graph->indegree[j]++;
			}
		}
	}
}

/**********************************************
 *
 * 拓扑排序
 *
 **********************************************/
bool TopLogicalSort(Graph *graph)
{
	int i, j, k;
	for(i = 0; i < graph->vernum; i++)
	{
		j = 0;
		while(graph->indegree[j] != 0)
		{
			j++;
			if(j > graph->vernum)
				return false;
		}

		topsort[i] = j;
		graph->indegree[j] = -1; //设置已拓扑排序顶点的入度为-1
		for(k = 0; k < graph->vernum; k++)
		{
			if(graph->edges[j][k] == 1)
				graph->indegree[k]--; //对j号顶点的每个邻接点的入度减1
		}
	}

	return true;
}

int main()
{
	Graph *graph = (Graph *)malloc(sizeof(Graph));
	MakeGraph(graph);
	GetInDegree(graph);

	if(TopLogicalSort(graph))
	{
		printf("\n拓扑排序序列为:\n");
		for(int i = 0; i < graph->vernum; i++)
		{
			printf("%c ", graph->vertexs[topsort[i]]);
		}
		printf("\n");
	}
	else
	{
		printf("图中存在环\n");
	}

	return 0;
}


以上代码测试结果如下图:



代码中如存在问题,望指正。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值