拓扑排序和关键路径课程设计
目录
1. 设计任务书... 3
1.1设计任务... 3
1.2程序功能... 3
1.3运行环境... 3
2. 本组课题... 3
2.1课题... 3
2.2本人任务... 3
3.程序功能简介... 4
3.1拓扑排序算法分析... 4
3.2关键路径算法分析... 4
4.功能实现分析... 5
4.1拓扑排序功能... 5
4.1.1具体实例... 5
4.1.2程序流程图... 6
4.1.3关键代码... 6
4.2关键路径功能... 7
4.2.1具体实例... 7
4.2.2程序流程图... 8
4.2.3关键代码... 8
4.3设计过程浅谈... 10
4.4源代码... 11
4.4.1拓扑排序... 11
4.4.2拓扑排序程序运行截图... 18
4.4.3关键路径... 18
4.4.4关键路径程序运行截图... 25
- 设计任务书
1.1设计任务
阅读了《数据结构(C语言)》的经典著作后,学习了有关简单算法的实现,认识到数学可
以应用到各个领域。本次算法课程设计运用所学的图论的拓扑排序和关键路径,去实现工程中的花费时间和顺利进行问题。拓扑排序主要用于检验工程能否施工,关键路径主要用于看出工程施工时间消耗。
1.2程序功能
本程序可以对给定输入的图进行拓扑排序以及利用拓扑排序的到的序列进行关键路径的求解,程序采用C语言代码实现,用到链表、图论等相关知识构成。
1.3运行环境
运行系统:Windows 10、Centos 7;
运行环境:C;
编译环境:CodeBlocks、Sublime Text 3;
- 本组课题
2.1课题
编写数据结构广泛使用拓扑排序和关键路径算法。
2.2本人任务
使用代码实现拓扑排序,进而利用拓扑排序得到的序列球的关键路径。
3.程序功能简介
3.1拓扑排序算法分析
用顶点表示活动,用弧表示活动间的优先关系的有向图为AOV网。
AOV网中没有环,检测的办法是进行拓扑排序。
步骤:
(1)在有向图中选一个没有前驱的顶点且输出之。
(2)从图中删除该顶点和所有以它为尾的弧。
重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。另一种情况则说明有向图中存在环。
3.2关键路径算法分析
与AOV网对应的是AOE网,即用边表示活动的网,AOE网是一个带权的有向无环图,顶点表示事件,弧表示活动,权表示活动持续的时间。通常用AOE网估计工程的完成时间。
由于在AOE网中有些活动可以并行的进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度。路径最长的叫关键路径,我们用e(i)表示活动最早开始时间,l(i)表示最迟开始时间。两者之差l(i)一e(i)意味着完成活动a的时间余量。我们把l(i)=e(i)的活动叫做关键活动。显然;关键路径上的所有活动都是关键活动,因此提前完成非关键活动并不能加快工程的进度。
4.功能实现分析
4.1拓扑排序功能
4.1.1具体实例
1 |
4 |
6 |
2 |
3 |
5 |
3 |
4 |
2 |
5 |
2 |
5 |
5 |
1 |
4 |
6 |
1 |
2 |
4 |
3 |
5 |
实例所示为拓扑排序过程。
最终得到的拓扑序列:V1-V6-V4-V3-V2-V5
图的基本操作 |
4.1.2程序流程图
图的邻接存储 |
TopologicalSort(ALGraph G) |
得到拓扑序列 |
4.1.3关键代码
Status TopologicalSort(ALGraph G)
{ /* 有向图G采用邻接表存储结构。若G无回路,则输出G的顶点的一个拓扑序列并返回OK, */
/* 否则返回ERROR。算法7.12 */
int i,k,count,indegree[MAX_VERTEX_NUM];
SqStack S;
ArcNode *p;
FindInDegree(G,indegree); /* 对各顶点求入度indegree[0..vernum-1] */
InitStack(&S); /* 初始化栈 */
for(i=0;i<G.vexnum;++i) /* 建零入度顶点栈S */
if(!indegree[i])
Push(&S,i); /* 入度为0者进栈 */
count=0; /* 对输出顶点计数 */
while(!StackEmpty(S))
{ /* 栈不空 */
Pop(&S,&i);
printf("%s ",G.vertices[i].data); /* 输出i号顶点并计数 */
++count;
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
{ /* 对i号顶点的每个邻接点的入度减1 */
k=p->adjvex;
if(!(--indegree[k])) /* 若入度减为0,则入栈 */
Push(&S,k);
}
}
if(count<G.vexnum)
{
printf("此有向图有回路\n");
return ERROR;
}
else
{
printf("为一个拓扑序列。\n");
return OK;
}
}
4.2关键路径功能
4.2.1具体实例
栈操作 |
图的邻接表 |
4.2.2程序流程图
图的基本操作 |
广度优先遍历 |
StatusTopologicalSort(ALGraph G) |
求得关键路径 |
队列操作 |
拓扑排序 |
4.2.3关键代码
Status TopologicalOrder(ALGraph G,SqStack *T)
{ /* 算法7.13 有向网G采用邻接表存储结构,求各顶点事件的最早发生时间ve */
/* (全局变量)。T为拓扑序列顶点栈,S为零入度顶点栈。若G无回路,则用栈T */
/* 返回G的一个拓扑序列,且函数值为OK,否则为ERROR */
int j,k,count,indegree[MAX_VERTEX_NUM];
SqStack S;
ArcNode *p;
FindInDegree(G,indegree);/*对各顶点求入度indegree[0..vernum-1] */
InitStack(&S); /* 初始化栈 */
for(j=0;j<G.vexnum;++j) /* 建零入度顶点栈S */
if(!indegree[j])
Push(&S,j); /* 入度为0者进栈 */
InitStack(T); /* 初始化拓扑序列顶点栈 */
count=0; /* 对输出顶点计数 */
for(j=0;j<G.vexnum;++j) /* 初始化ve[]=0 (最小值) */
ve[j]=0;
while(!StackEmpty(S))
{ /* 栈不空 */
Pop(&S,&j);
Push(T,j); /* j号顶点入T栈并计数 */
++count;
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{ /* 对j号顶点的每个邻接点的入度减1 */
k=p->adjvex;
if(--indegree[k]==0) /* 若入度减为0,则入栈 */
Push(&S,k);
if(ve[j]+*(p->info)>ve[k])
ve[k]=ve[j]+*(p->info);
}
}
if(count<G.vexnum)
{
printf("此有向网有回路\n");
return ERROR;
}
else
return OK;
}
Status CriticalPath(ALGraph G)
{ /* 算法7.14 G为有向网,输出G的各项关键活动 */
int vl[MAX_VERTEX_NUM];
SqStack T;
int i,j,k,ee,el;
ArcNode *p;
char dut,tag;
if(!TopologicalOrder(G,&T)) /* 产生有向环 */
return ERROR;
j=ve[0];
for(i=1;i<G.vexnum;i++) /* j=Max(ve[]) 完成点的值 */
if(ve[i]>j)
j=ve[i];
for(i=0;i<G.vexnum;i++) /* 初始化顶点事件的最迟发生时间(最大值) */
vl[i]=j; /* 完成点的最早发生时间 */
while(!StackEmpty(T)) /* 按拓扑逆序求各顶点的vl值 */
for(Pop(&T,&j),p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info); /* dut<j,k> */
if(vl[k]-dut<vl[j])
vl[j]=vl[k]-dut;
}
printf(" j k dut ee el tag\n");
for(j=0;j<G.vexnum;++j) /* 求ee,el和关键活动 */
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info);
ee=ve[j];
el=vl[k]-dut;
tag=(ee==el)?'*':' ';
printf("%2d %2d %3d %3d %3d %c\n",j,k,dut,ee,el,tag); /* 输出关键活动 */
}
printf("关键活动为:\n");
for(j=0;j<G.vexnum;++j) /* 同上 */
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info);
if(ve[j]==vl[k]-dut)
printf("%s→%s\n",G.vertices[j].data,G.vertices[k].data); /* 输出关键活动 */
}
return OK;
}
4.3设计过程浅谈
在大一一学年的时间,初识了C语言和JAVA语言,学习了编程的基本功,认识到技术的道路漫长而遥远,只有不断的通过自学和向别人学习,不断保持进步的节奏。提高人生视野来认识更多的编程方法,去创新。
学习是需要付出时间和精力的,一直有输入才能厚积薄发,在学习数据结构课程时,一开始也是感觉很难,不知道无从下手,曾经纠结迷茫也失去信心。但随着时间的推进,好多困难渐渐克服,发现温故而知新的力量是真的强大。从一开始认识算法的时间复杂度和空间复杂度,进一步认识到线性结构和非线性结构,又从中学习了链表、栈、队列、串,到后来的树和图论。这些东西循序渐进,慢慢的组成了一张自己对于数据结构的知识网络。
“路漫漫其修远兮,吾将上下而求索”是教会我们学无止境,书山有路,勤能补拙大丈夫。
4.4源代码
4.4.1拓扑排序
#include<string.h>
#include<malloc.h> /* malloc()等 */
#include<limits.h> /* INT_MAX等 */
#include<stdio.h> /* EOF(=^Z或F6),NULL */
#include<stdlib.h> /* atoi() */
#include<math.h> /* floor(),ceil(),abs() */
/* 函数结果状态代码 */
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
#define MAX_NAME 5 /* 顶点字符串的最大长度 */
typedef int InfoType;
typedef char VertexType[MAX_NAME]; /* 字符串类型 */
/* 图的邻接表存储表示 */
#define MAX_VERTEX_NUM 20
/*网和图的区别是图没有权值,网有权值*/
typedef enum{DG,DN,AG,AN}GraphKind; /* 用枚举类型建立图的种类 {有向图,有向网,无向图,无向网} */
typedef struct ArcNode
{
int adjvex; /* 该弧所指向的顶点的位置 */
struct ArcNode *nextarc; /* 指向下一条弧的指针 */
InfoType *info; /* 网的权值指针) */
}ArcNode; /* 表结点 */
typedef struct
{
VertexType data; /* 顶点信息 */
ArcNode *firstarc; /* 第一个表结点的地址,指向第一条依附该顶点的弧的指针 */
}VNode,AdjList[MAX_VERTEX_NUM]; /* 头结点 */
typedef struct
{
AdjList vertices;
int vexnum,arcnum; /* 图的当前顶点数和弧数 */
int kind; /* 图的种类标志 */
}ALGraph;
/* 图的邻接表存储的基本操作(15个) */
int LocateVex(ALGraph G,VertexType u)
{ /* 初始条件: 图G存在,u和G中顶点有相同特征 */
/* 操作结果: 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1 */
int i;
for(i=0;i<G.vexnum;++i)
if(strcmp(u,G.vertices[i].data)==0) //比较顶点是否相等
return i;
return -1;
}
Status CreateGraph(ALGraph *G)
{ /* 采用邻接表存储结构,构造没有相关信息的图G(用一个函数构造4种图) */
int i,j,k;
int w; /* 权值 */
VertexType va,vb;
ArcNode *p;
printf("请输入图的类型(有向图:0,有向网:1,无向图:2,无向网:3): ");
scanf("%d",&(*G).kind);
printf("请输入图的顶点数,边数: ");
scanf("%d,%d",&(*G).vexnum,&(*G).arcnum);
printf("请输入%d个顶点的值(<%d个字符):\n",(*G).vexnum,MAX_NAME);
for(i=0;i<(*G).vexnum;++i) /* 构造顶点向量 */
{
scanf("%s",(*G).vertices[i].data);
(*G).vertices[i].firstarc=NULL;
}
if((*G).kind==1||(*G).kind==3) /* 网 */
printf("请顺序输入每条弧(边)的权值、弧尾和弧头(以空格作为间隔):\n");
else /* 图 */
printf("请顺序输入每条弧(边)的弧尾和弧头(以空格作为间隔):\n");
for(k=0;k<(*G).arcnum;++k) /* 构造表结点链表 */
{
if((*G).kind==1||(*G).kind==3) /* 网 */
scanf("%d%s%s",&w,va,vb);
else /* 图 */
scanf("%s%s",va,vb);
i=LocateVex(*G,va); /* 弧尾 */
j=LocateVex(*G,vb); /* 弧头 */
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=j;
if((*G).kind==1||(*G).kind==3) /* 网 */
{
p->info=(int *)malloc(sizeof(int));
*(p->info)=w; //网的权值指针
}
else
p->info=NULL; /* 图 */
p->nextarc=(*G).vertices[i].firstarc; /* 插在表头 */
(*G).vertices[i].firstarc=p;
if((*G).kind>=2) /* 无向图或网,产生第二个表结点 */
{
p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=i;
if((*G).kind==3) /* 无向网 */
{
p->info=(int*)malloc(sizeof(int));
*(p->info)=w;
}
else
p->info=NULL; /* 无向图 */
p->nextarc=(*G).vertices[j].firstarc; /* 插在表头 */
(*G).vertices[j].firstarc=p;
}
}
return OK;
}
void Display(ALGraph G)
{ /* 输出图的邻接矩阵G */
int i;
ArcNode *p;
switch(G.kind)
{
case DG: printf("有向图\n");
break;
case DN: printf("有向网\n");
break;
case AG: printf("无向图\n");
break;
case AN: printf("无向网\n");
}
printf("%d个顶点:\n",G.vexnum);
for(i=0;i<G.vexnum;++i)
printf("%s ",G.vertices[i].data);
printf("\n%d条弧(边):\n",G.arcnum);
for(i=0;i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
if(G.kind<=1) /* 有向 */
{
printf("%s→%s ",G.vertices[i].data,G.vertices[p->adjvex].data);
if(G.kind==DN) /* 网 */
printf(":%d ",*(p->info));
}
else /* 无向(避免输出两次) */
{
if(i<p->adjvex)
{
printf("%s-%s ",G.vertices[i].data,G.vertices[p->adjvex].data);
if(G.kind==AN) /* 网 */
printf(":%d ",*(p->info));
}
}
p=p->nextarc;
}
printf("\n");
}
}
void FindInDegree(ALGraph G,int indegree[])
{ /* 求顶点的入度,算法7.12、7.13调用 */
int i;
ArcNode *p;
for(i=0;i<G.vexnum;i++)
indegree[i]=0; /* 赋初值 */
for(i=0;i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
typedef int SElemType; /* 栈类型 */
#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
SElemType *top; /* 栈顶指针 */
int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */
/* 顺序栈的基本操作(9个) */
Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
(*S).base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top=(*S).base;
(*S).stacksize=STACK_INIT_SIZE;
return OK;
}
Status DestroyStack(SqStack *S)
{ /* 销毁栈S,S不再存在 */
free((*S).base);
(*S).base=NULL;
(*S).top=NULL;
(*S).stacksize=0;
return OK;
}
Status ClearStack(SqStack *S)
{ /* 把S置为空栈 */
(*S).top=(*S).base;
return OK;
}
Status StackEmpty(SqStack S)
{ /* 若栈S为空栈,则返回TRUE,否则返回FALSE */
if(S.top==S.base)
return TRUE;
else
return FALSE;
}
int StackLength(SqStack S)
{ /* 返回S的元素个数,即栈的长度 */
return S.top-S.base;
}
Status GetTop(SqStack S,SElemType *e)
{ /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */
if(S.top>S.base)
{
*e=*(S.top-1);
return OK;
}
else
return ERROR;
}
Status Push(SqStack *S,SElemType e)
{ /* 插入元素e为新的栈顶元素 */
if((*S).top-(*S).base>=(*S).stacksize) /* 栈满,追加存储空间 */
{
(*S).base=(SElemType *)realloc((*S).base,((*S).stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top=(*S).base+(*S).stacksize;
(*S).stacksize+=STACKINCREMENT;
}
*((*S).top)++=e;
return OK;
}
Status Pop(SqStack *S,SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
if((*S).top==(*S).base)
return ERROR;
*e=*--(*S).top;
return OK;
}
Status StackTraverse(SqStack S,Status(*visit)(SElemType))
{ /* 从栈底到栈顶依次对栈中每个元素调用函数visit()。 */
/* 一旦visit()失败,则操作失败 */
while(S.top>S.base)
visit(*S.base++);
printf("\n");
return OK;
}
Status TopologicalSort(ALGraph G)
{ /* 有向图G采用邻接表存储结构。若G无回路,则输出G的顶点的一个拓扑序列并返回OK, */
/* 否则返回ERROR。算法7.12 */
int i,k,count,indegree[MAX_VERTEX_NUM];
SqStack S;
ArcNode *p;
FindInDegree(G,indegree); /* 对各顶点求入度indegree[0..vernum-1] */
InitStack(&S); /* 初始化栈 */
for(i=0;i<G.vexnum;++i) /* 建零入度顶点栈S */
if(!indegree[i])
Push(&S,i); /* 入度为0者进栈 */
count=0; /* 对输出顶点计数 */
printf("(");
while(!StackEmpty(S))
{ /* 栈不空 */
Pop(&S,&i);
printf("%s ",G.vertices[i].data); /* 输出i号顶点并计数 */
++count;
for(p=G.vertices[i].firstarc;p;p=p->nextarc)
{ /* 对i号顶点的每个邻接点的入度减1 */
k=p->adjvex;
if(!(--indegree[k])) /* 若入度减为0,则入栈 */
Push(&S,k);
}
}
printf(")");
if(count<G.vexnum)
{
printf("此有向图有回路\n");
return ERROR;
}
else
{
printf("为一个拓扑序列。\n");
return OK;
}
}
void main()
{
ALGraph f;
printf("请选择有向图\n");
CreateGraph(&f);
Display(f);
TopologicalSort(f);
}
4.4.2拓扑排序程序运行截图
4.4.3关键路径
void Display(ALGraph G)
{ /* 输出图的邻接矩阵G */
int i;
ArcNode *p;
switch(G.kind)
{
case DG: printf("有向图\n");
break;
case DN: printf("有向网\n");
break;
case AG: printf("无向图\n");
break;
case AN: printf("无向网\n");
}
printf("%d个顶点:\n",G.vexnum);
for(i=0;i<G.vexnum;++i)
printf("%s ",G.vertices[i].data);
printf("\n%d条弧(边):\n",G.arcnum);
for(i=0;i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
if(G.kind<=1) /* 有向 */
{
printf("%s→%s ",G.vertices[i].data,G.vertices[p->adjvex].data);
if(G.kind==DN) /* 网 */
printf(":%d ",*(p->info));
}
else /* 无向(避免输出两次) */
{
if(i<p->adjvex)
{
printf("%s-%s ",G.vertices[i].data,G.vertices[p->adjvex].data);
if(G.kind==AN) /* 网 */
printf(":%d ",*(p->info));
}
}
p=p->nextarc;
}
printf("\n");
}
}
int ve[MAX_VERTEX_NUM]; /* 全局变量(用于算法7.13和算法7.14) */
void FindInDegree(ALGraph G,int indegree[])
{ /* 求顶点的入度,算法7.12、7.13调用 */
int i;
ArcNode *p;
for(i=0;i<G.vexnum;i++)
indegree[i]=0; /* 赋初值 */
for(i=0;i<G.vexnum;i++)
{
p=G.vertices[i].firstarc;
while(p)
{
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
typedef int SElemType; /* 栈类型 */
#define STACK_INIT_SIZE 10 /* 存储空间初始分配量 */
#define STACKINCREMENT 2 /* 存储空间分配增量 */
typedef struct SqStack
{
SElemType *base; /* 在栈构造之前和销毁之后,base的值为NULL */
SElemType *top; /* 栈顶指针 */
int stacksize; /* 当前已分配的存储空间,以元素为单位 */
}SqStack; /* 顺序栈 */
/* 顺序栈的基本操作(9个) */
Status InitStack(SqStack *S)
{ /* 构造一个空栈S */
(*S).base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top=(*S).base;
(*S).stacksize=STACK_INIT_SIZE;
return OK;
}
Status DestroyStack(SqStack *S)
{ /* 销毁栈S,S不再存在 */
free((*S).base);
(*S).base=NULL;
(*S).top=NULL;
(*S).stacksize=0;
return OK;
}
Status ClearStack(SqStack *S)
{ /* 把S置为空栈 */
(*S).top=(*S).base;
return OK;
}
Status StackEmpty(SqStack S)
{ /* 若栈S为空栈,则返回TRUE,否则返回FALSE */
if(S.top==S.base)
return TRUE;
else
return FALSE;
}
int StackLength(SqStack S)
{ /* 返回S的元素个数,即栈的长度 */
return S.top-S.base;
}
Status GetTop(SqStack S,SElemType *e)
{ /* 若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR */
if(S.top>S.base)
{
*e=*(S.top-1);
return OK;
}
else
return ERROR;
}
Status Push(SqStack *S,SElemType e)
{ /* 插入元素e为新的栈顶元素 */
if((*S).top-(*S).base>=(*S).stacksize) /* 栈满,追加存储空间 */
{
(*S).base=(SElemType *)realloc((*S).base,((*S).stacksize+STACKINCREMENT)*sizeof(SElemType));
if(!(*S).base)
exit(OVERFLOW); /* 存储分配失败 */
(*S).top=(*S).base+(*S).stacksize;
(*S).stacksize+=STACKINCREMENT;
}
*((*S).top)++=e;
return OK;
}
Status Pop(SqStack *S,SElemType *e)
{ /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
if((*S).top==(*S).base)
return ERROR;
*e=*--(*S).top;
return OK;
}
Status StackTraverse(SqStack S,Status(*visit)(SElemType))
{ /* 从栈底到栈顶依次对栈中每个元素调用函数visit()。 */
/* 一旦visit()失败,则操作失败 */
while(S.top>S.base)
visit(*S.base++);
printf("\n");
return OK;
}
Status TopologicalOrder(ALGraph G,SqStack *T)
{ /* 算法7.13 有向网G采用邻接表存储结构,求各顶点事件的最早发生时间ve */
/* (全局变量)。T为拓扑序列顶点栈,S为零入度顶点栈。若G无回路,则用栈T */
/* 返回G的一个拓扑序列,且函数值为OK,否则为ERROR */
int j,k,count,indegree[MAX_VERTEX_NUM];
SqStack S;
ArcNode *p;
FindInDegree(G,indegree);/*对各顶点求入度indegree[0..vernum-1] */
InitStack(&S); /* 初始化栈 */
for(j=0;j<G.vexnum;++j) /* 建零入度顶点栈S */
if(!indegree[j])
Push(&S,j); /* 入度为0者进栈 */
InitStack(T); /* 初始化拓扑序列顶点栈 */
count=0; /* 对输出顶点计数 */
for(j=0;j<G.vexnum;++j) /* 初始化ve[]=0 (最小值) */
ve[j]=0;
while(!StackEmpty(S))
{ /* 栈不空 */
Pop(&S,&j);
Push(T,j); /* j号顶点入T栈并计数 */
++count;
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{ /* 对j号顶点的每个邻接点的入度减1 */
k=p->adjvex;
if(--indegree[k]==0) /* 若入度减为0,则入栈 */
Push(&S,k);
if(ve[j]+*(p->info)>ve[k])
ve[k]=ve[j]+*(p->info);
}
}
if(count<G.vexnum)
{
printf("此有向网有回路\n");
return ERROR;
}
else
return OK;
}
Status CriticalPath(ALGraph G)
{ /* 算法7.14 G为有向网,输出G的各项关键活动 */
int vl[MAX_VERTEX_NUM];
SqStack T;
int i,j,k,ee,el;
ArcNode *p;
char dut,tag;
if(!TopologicalOrder(G,&T)) /* 产生有向环 */
return ERROR;
j=ve[0];
for(i=1;i<G.vexnum;i++) /* j=Max(ve[]) 完成点的值 */
if(ve[i]>j)
j=ve[i];
for(i=0;i<G.vexnum;i++) /* 初始化顶点事件的最迟发生时间(最大值) */
vl[i]=j; /* 完成点的最早发生时间 */
while(!StackEmpty(T)) /* 按拓扑逆序求各顶点的vl值 */
for(Pop(&T,&j),p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info); /* dut<j,k> */
if(vl[k]-dut<vl[j])
vl[j]=vl[k]-dut;
}
printf(" j k dut ee el tag\n");
for(j=0;j<G.vexnum;++j) /* 求ee,el和关键活动 */
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info);
ee=ve[j];
el=vl[k]-dut;
tag=(ee==el)?'*':' ';
printf("%2d %2d %3d %3d %3d %c\n",j,k,dut,ee,el,tag); /* 输出关键活动 */
}
printf("关键活动为:\n");
for(j=0;j<G.vexnum;++j) /* 同上 */
for(p=G.vertices[j].firstarc;p;p=p->nextarc)
{
k=p->adjvex;
dut=*(p->info);
if(ve[j]==vl[k]-dut)
printf("%s→%s\n",G.vertices[j].data,G.vertices[k].data); /* 输出关键活动 */
}
return OK;
}
void main()
{
ALGraph h;
printf("请选择有向网\n");
CreateGraph(&h);
Display(h);
CriticalPath(h);
}
4.4.4关键路径程序运行截图
拓扑排序和关键路径课程设计相关推荐
- 拓扑排序和关键路径的图形化显示
资源下载地址:https://download.csdn.net/download/sheziqiong/85883662 资源下载地址:https://download.csdn.net/downl ...
- 考研复习之数据结构笔记(十二)图(下)(图的应用,包含最小生成树、最短路径、拓扑排序、关键路径以及单元小结)
目录 一.图的应用 1.1 最小生成树 (1)基本概念与问题引入 (2)Prim(普里姆)算法 (3)Kruskal(克鲁斯卡尔)算法 1.2 最短路径 (1)基本概念与问题引入 (2)Dijkstr ...
- 数据结构-----图的拓扑排序和关键路径算法
部分图片取自:http://www.cnblogs.com/navorse/articles/1893863.html 在介绍拓扑排序和关键路径之前,先引入AOE网络的概念: 该图为一个AOE网,顶点 ...
- 大话数据结构 第七章 图(二) 最小生成树、最短路径、拓扑排序、关键路径算法
大话数据结构 第七章 图(二) 最小生成树.最短路径.拓扑排序.关键路径算法 最小生成树 定义 Prim算法 Kruskal算法 最短路径 Dijkstra算法 Floyd算法 拓扑排序 AOV网 拓 ...
- 超详细讲解实现拓扑排序、关键路径
今天,小编带着大家来学习图中非常重要的一环,拓扑排序和关键路径! 目录 一. 绪论--实际应用 二. 拓扑排序 (一).含义 (二).实现原理 (三).代码实现 三. 关键路径 (一).含义 (二). ...
- 数据结构(六):图的概念、存储方式、基本操作、最小生成树、最短路径、有向无环图、关键路径 | Prim、Kruskal算法 | BFS、Dijkstra、Floyd算法 | 拓扑排序 | 求关键路径
文章目录 第六章 图 一.图 (一)图的定义 (二)图逻辑结构的应用 (三)无向图.有向图 (四)简单图.多重图 (五)顶点的度.入度.出度 (六)顶点-顶点的关系描述 (七)连通图.强连通图 (八) ...
- 【数据结构-图】4.拓扑排序和关键路径(注解+原理)
一.拓扑排序 1.1 基本知识 有向无环图:一个有向图中不存在环,简称DAG图 AOV网:用DAG图表示一个工程,其顶点表示活动,用有向边 <Vi,Vj><V_i, V_j>& ...
- BUCT数据结构——图(拓扑排序、关键路径)
文章目录 问题 A: 邻接矩阵存储的图,节点的出度和入度计算(附加代码模式) 问题 B: 算法7-12:有向无环图的拓扑排序 问题 C: 有向图是否存在环? 问题 D: 图-节点的最早发生时间 问题 ...
- 拓扑排序与关键路径(AOV网和AOE网)
一.AOV网(Activity On Vertex Network) 在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,称为AOV网. 不能存在回路 ...
最新文章
- 如何禁止浏览器自动填充
- 前端(HTML/CSS/JS)-CSS编码规范
- 1.2 Object类详解
- 获取昨天凌晨毫秒数_Java 获取当前时间距离当天凌晨的秒数
- mysql alert table 日志_MySQL日志
- jsf按钮响应事件_如何从JSF获取JSON响应?
- 云+X案例展 | 传播类:九州云 SD-WAN 携手上海电信,助力政企客户网络重构换新颜...
- SSM 整合 4:Spring IoC 容器基于的两个重要接口 BeanFactory 和 ApplicationContext
- 强化学习《基于价值 - Double Q-Learning》
- 1TB存储版iPhone 13 Pro机型预计交付时间已被推迟至10月
- 蓝桥杯2016年C/C++ 混搭
- 一个nginx 502问题解决方案
- 2020年蓝桥杯省赛 C++ B组
- 什么是即席查询及即席查询实现
- html 中英文字体自动调节,网页CSS字体/网页常用中英文字体(带字体预览)
- c语言考研必刷题小程序,小程序推荐:大学生必备刷题小程序,内容涵盖各种证书考试题型...
- django之 将字典数据导入数据库以及解决insert自动增长的id不是从1开始或不连续
- NFA到DFA的子集构造法
- imresize导入错误ImportError: cannot import name 'imresize'
- 百择电商:抖音什么情况下会被限流?
热门文章
- 搞懂进程组、会话、控制终端关系,才能明白守护进程干嘛的?
- 【干货】同步与互斥的失败例子
- 指针的基本操作(10.1 Basic Pointer Operations)
- java基础语句_【Java基础-Java语言基础】
- 泛型类有什么作用_3 分钟带你彻底搞懂 Java 泛型背后的秘密
- android libc 有哪些函数_Android scudo功能介绍
- 【Pytorch神经网络理论篇】 17 循环神经网络结构:概述+BP算法+BPTT算法
- 手写实现简单的Vue事件总线
- Web框架——Flask系列之请求上下文与应用上下文请求钩子Flask-Script扩展命令行(十七)
- LeetCode 2201. 统计可以提取的工件(哈希)