拓扑排序
- 应用领域
(1)确定各项活动在整个工程执行中的先后顺序(即拓扑排序序列)。
(2)判定工程的可行性。若有回路则工程无法结束(不可行);
(3)顶点表示课程;有向边表示课程间的制约关系。
(4)例题:
D - AOV网(顶点表示活动的网)
在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系。
AOV网中出现回路意味着什么?
answer:活动之间的优先关系是矛盾的
- 基本概念
(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 - 拓扑排序算法
(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 的入度减 1;
3.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;
}