拓扑排序(知识及代码实现)

拓扑排序

  1. 应用领域
    (1)确定各项活动在整个工程执行中的先后顺序(即拓扑排序序列)。
    (2)判定工程的可行性。若有回路则工程无法结束(不可行);
    AOV网
    (3)顶点表示课程;有向边表示课程间的制约关系。
    在这里插入图片描述
    在这里插入图片描述
    (4)例题:
    在这里插入图片描述
    D
  2. AOV网(顶点表示活动的网)
    在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系。

AOV网中出现回路意味着什么?
answer:活动之间的优先关系是矛盾的

  1. 基本概念
    (1)拓扑序列:
    设有向图G=(V,E)具有 n 个顶点,则顶点序列 v0, v1, …, vn-1 称为一个拓扑序列,当且仅当满足下列条件:若从顶点 vi到 vj 有一条路径,则在顶点序列中顶点 vi 必在顶点 vj 之前。
    使得AOV网中所有应该存在的前驱和后继关系都能得到满足
    (2)拓扑排序:
    在这里插入图片描述
    拓扑序列 1:v0 v1 v2 v3 v4 v5 v6
    拓扑序列 2:v0 v1 v3 v2 v4 v5 v6
  2. 拓扑排序算法
    (1)算法:
  算法:拓扑排序TopSort
  输入:AOV网 G=(V,E)
  输出:拓扑序列
          1. 重复下述操作,直到输出全部顶点,或AOV网中不存在没有前驱的顶点
              1.1  从AOV网中选择一个“没有前驱”的顶点并且输出;
              1.2  从AOV网中删去该顶点,并且删去所有以该顶点为“尾”的弧;

尾:在这里插入图片描述

(2)实现前提:
(a)采用邻接表
(b)选择没有前驱的结点——结点的入度为0——如何求入度?——顶点表中增加入度域。
(c)如何查找没有前驱的结点?——设置栈或队列
在这里插入图片描述
(3)伪代码:
图:带入度的邻接表
栈:入度为0的顶点编号

算法:TopSort
输入:有向图G=(V,E)
输出:拓扑序列
  1. 栈 S 初始化;
     累加器 count 初始化;
  2. 扫描顶点表,将入度为 0 的“顶点”压栈;
  3. 当“栈 S 非空”时循环
       3.1 j = 栈顶元素出栈;
           输出顶点 j;
           count++3.2 对顶点 j 的每一个邻接点 k 执行下述操作:
             3.2.1 将顶点 k 的入度减 13.2.2 如果顶点 k 的入度为 0,则将顶点 k 入栈;
  4. if (count<vertexNum) 输出有回路信息;

(4)C实现片段:

void TopSort(ALGraph *G)
{
    int i, j, k, count = 0,  S[MaxSize], top = -1; 
    EdgeNode *p = NULL;
    for (i = 0; i < G->vertexNum; i++)  /*扫描顶点表*/ 
    {
    	if (G->adjlist[i].in == 0)  S[++top] = i;
    }
	while (top != -1 ) /*当栈中还有入度为0的顶点时*/
    {
        j = S[top--];/*输出栈顶元素*/
        printf("%c ", G->adjlist[j].vertex);     
        count++;
        
        p = G->adjlist[j].first;        
        while (p != NULL)/*描顶点表,找出顶点j的所有出边*/
        {
            k = p->adjvex;  
            G->adjlist[k].in--;            
            if (G->adjlist[k].in == 0)  /*将入度为0的顶点入栈*/
            {
            	S[++top] = k; 
            }
            p=p->next;
        }
    }
    if (count < G->vertexNum )  printf("有回路");
}

时间复杂度:O(n+e)

5.例题:

【问题描述】
1)问题描述:软件专业的学生要学习一系列课程,其中有些课程必须在其先修课完成后才能学习。
2)实验要求:假设每门课程的学习“时间为一个学期”,试为该专业的学生设计教学计划,使他们能在“最短时间”内修完专业要求的全部课程。
3) 实现提示:
   以“顶点代表课程”,弧代表课程的先后修关系,按课程先后关系建立有向无环图。
   利用拓扑排序实现。
【输入形式】
首先,输入一个n表示有n门课程(1-n)为编号,再输入一个m表示课程关系,接下来m行每行输入两个整数a b,表示必须修完a才可以修b。元素之间以空格分隔。
【输出形式】
输出一个选修顺序(拓扑排序,输出结点之间有一空格),“如果在某一时刻存在多个输出,输出编号最小的课程。”
//正确 
#include<stdio.h>
#include<stdlib.h>
#define MaxSize 10
//为了好定义,下面输入的n不能在现在用于结构体里数组的定义 

//1.存储结构的定义
//1)边表---邻接点组成的单链表
typedef struct EdgeNode
{
	int adjvex;
	struct EdgeNode *next;
}EdgeNode; 
//2)顶点表 
typedef struct
{
	int vertex;
	int in; 
	EdgeNode *first;//统一格式 
}VertexNode;
//3)整体定义
typedef struct
{
	VertexNode adjlist[MaxSize];//MaxSize=n相当于顶点的个数
	int vertexNum;//vertexNum==n;
	int edgeNum;//edgeNum==m; 
}ALGraph; 
int visited[MaxSize]={0};
//2.图的建立 
ALGraph CreatGraph(ALGraph G,int n,int m)
			  /*图:G(有一个顶点表) 
				int n,int m:将点和边的数量传递过来 
			  */ 
{
	int i,a,b;
	EdgeNode *s=NULL;
	G.vertexNum=n;
	G.edgeNum=m;
	//建立一个顶点表
	for(i=1;i<=G.vertexNum;i++)
	{
		G.adjlist[i].vertex=i;
		G.adjlist[i].first=NULL;
		G.adjlist[i].in=0;
	} 
	//完善边表——直接头插
	for(i=1;i<=G.edgeNum;i++)
	{
		scanf("%d %d",&a,&b);
		G.adjlist[b].in++;
		
		s=(EdgeNode*)malloc(sizeof(EdgeNode));
		s->adjvex=b;
		s->next=G.adjlist[a].first;
		G.adjlist[a].first=s;
	}
	return G;
} 
void TopSort(ALGraph G)
{
    int i,j,k,count=0,S[MaxSize],top=0;
    int min;
    EdgeNode *p = NULL;
    for (i = 1; i <= G.vertexNum; i++)  /*扫描顶点表*/ 
    {
    	if(G.adjlist[i].in == 0)  
		{
			S[++top] = i;
		}
    }
    count=top;
	while (count != 0 ) 
    {
        min=MaxSize;
        for(i=1;i<=top;i++)
		{
			if(S[i]<min)
			{
				min=S[i];
				j=i;	
			}	
		}
		S[j]=MaxSize;
		j=min;
		printf("%d ", G.adjlist[j].vertex);
		count--;    
        
        p = G.adjlist[j].first;        
        while (p != NULL)/*描顶点表,找出顶点j的所有出边*/
        {
            k = p->adjvex;  
            G.adjlist[k].in--;            
            if (G.adjlist[k].in == 0)  /*将入度为0的顶点入栈*/
            {
            	S[++top] = k; 
            	count++;
            }
            p=p->next;
        }
    }
}
int main()
{
	int n,m;//n个结点,m条边
	scanf("%d %d",&n,&m);
	//创建一个图 
    ALGraph G;
	G=CreatGraph(G,n,m);
	//遍历
	TopSort(G);

	return 0; 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值