数据结构(有向无环图(DAG)、AOV网、拓扑排序;关键路径、AOE网)

8.12、有向无环图(DAG)、AOV网、拓扑排序

有向无环图(DAG)

若一个有向图中不存在环,则称为有向无环图,简称DAG图(Directed Acyclic Graph).

AOV网

AOV网(Activity Vertex NetWork,用顶点表示活动的网),用DAG图(有向无环图)表示一个工程。顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行

拓扑排序:

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:

  • 每个顶点出现且只出现一次
  • 若顶点A在序列中排在顶点B的前面,则图中不存在从顶点B到顶点A的路径

或定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得若存在一条从顶点A到顶点B的路径,则B出现在A的后面,每个AOV网都有一个或者多个拓扑排序。

拓扑排序的实现

  1. 从AOV网中选择一个没有前驱(入度为0)的顶点并输出
  2. 从网中删除该顶点和所有以它为起点的有向边
  3. 重复1和2直到当前的AOV网为空或当前网中不存在无前驱的顶点为止

拓扑排序算法思想

拓扑排序的代码实现

#include <stdio.h>
#include <stdlib.h>
#include<math.h>
#define MaxSize 100
#define MaxInteger 32767
#define boolean int
#define true 1
#define false 0
//边\弧
typedef struct ArcNode{
int adjvex;//边或者弧指向的那个结点
struct ArcNode *next;//指向下一个弧的指针
int info;//权值
}ArcNode;
//顶点信息
typedef struct ElemType{
char v;//顶点信息
int flag;//顶点是否被使用,1表示使用,0表示未使用
}ElemType;
//顶点
typedef struct VNode{
ElemType data;//顶点信息
ArcNode *first;//第一个边或弧
}VNode,AdjList[MaxSize];
//有向图的邻接表存储
typedef struct MGraph{
AdjList vertices;//图的信息
int vexnum,arcnum;//顶点数和边数
}MGraph;
//初始化图
boolean MG_Init(MGraph **G){
(*G) = (MGraph *)malloc(sizeof(MGraph));
if(*G == NULL){
printf("内存申请失败!\n");
return false;
}
(*G)->arcnum = 0;//边数
(*G)->vexnum = 0;//顶点数
for(int i = 0;i < MaxSize;i++){//把所有带使用的顶点信息数据使用标志全置为0
(*G)->vertices[i].data.flag = 0;
(*G)->vertices[i].first = NULL;
}
return true;
}
//判断顶点V是否存在,并获取边的索引Vi
boolean IsVEmpty(MGraph *G,ElemType V,int *Vi){
for(int i = 0;i < G->vexnum;i++){
if(G->vertices[i].data.v == V.v && G->vertices[i].data.flag == 1) {
*Vi = i;
return true;
}
}
return false;
}
//判断边是否存在:
boolean Adjancent(MGraph *G,ElemType V,ElemType W,int *Vi,int *Wi){
if(!IsVEmpty(G,V,Vi) || !IsVEmpty(G,W,Wi)){
printf("其中一个顶点不存在!!/\n");
return true;
}
ArcNode *node = G->vertices[*Vi].first;
while(node != NULL){
if(node->adjvex == *Wi){
// printf("边已经存在\n");
return true;
}
node = node->next;
}
return false;
}
//插入顶点
boolean InsertV(MGraph *G,ElemType V){
int *Vi;
if(IsVEmpty(G,V,Vi)){
printf("顶点已经存在!!\n");
return false;
}
V.flag = 1;
G->vertices[G->vexnum++].data = V;
return true;
}
//插入边
boolean AddArcNode(MGraph *G,ElemType V,ElemType W,int info){
int *Vi = (int *)malloc(sizeof(int));
int *Wi = (int *)malloc(sizeof(int));
if(Adjancent(G,V,W,Vi,Wi)){//判断边是否存在
return false;
}
ArcNode * p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = *Wi;
p->info = info;
p->next = NULL;
if(G->vertices[*Vi].first == NULL){//没有边,新增加边
G->vertices[*Vi].first = p;
G->arcnum++;
return true;
}
ArcNode *node = G->vertices[*Vi].first;
while(node->next != NULL){
node = node->next;
}
node->next = p;
G->arcnum++;
return true;
}
//获取某个结点v的所有邻接边;<v,wi>;以v为尾巴
boolean NeighBors(MGraph *G,ElemType V,ElemType ***res,int **infos,int *length){
int *Vi = (int *)malloc(sizeof(int));
if(!IsVEmpty(G,V,Vi)){
return false;
}
ArcNode *node = G->vertices[*Vi].first;
while(node != NULL){
(*length)++;
node = node->next;
}
*res = (ElemType **)malloc(sizeof(ElemType)*(*length));
*infos = (int *)malloc(sizeof(ElemType)*(*length));
node = G->vertices[*Vi].first;
for(int i=0;i < (*length) ;i++){
(*res)[i] = (ElemType *)malloc(sizeof(ElemType)*3);
(*res)[i][0] = V;
(*res)[i][1] = G->vertices[node->adjvex].data;
(*infos)[i] = node->info;
node = node->next;
}
return true;
}
//移除该边<V,W>
boolean RemoveArcNode(MGraph *G,ElemType V,ElemType W){
int *Vi = (int *)malloc(sizeof(int));
int *Wi = (int *)malloc(sizeof(int));
if(!Adjancent(G,V,W,Vi,Wi)){//判断该边是否存在
return false;
}
ArcNode *delete = G->vertices[*Vi].first;//删除的结点
ArcNode *pre = NULL;//删除边的前一个结点
while(delete != NULL){//寻找删除的边
if(delete->adjvex == *Wi){
break;
}
pre = delete;
delete = delete->next;
}
if(pre == NULL){
G->vertices[*Vi].first = G->vertices[*Vi].first->next;
}else{
pre->next = delete->next;//修改指针
}
G->arcnum--;//修改边数
free(delete);//释放内存
return true;
}
//删除顶点
boolean DeleteV(MGraph *G,ElemType V){
int *Vi = (int *)malloc(sizeof(int));
if(!IsVEmpty(G,V,Vi)){
return false;
}
ArcNode *W = G->vertices[*Vi].first;
while(W != NULL){
RemoveArcNode(G,V,G->vertices[G->vertices[*Vi].first->adjvex].data);
W = G->vertices[*Vi].first;
}
int index = 0;
int sum = 0;
while(sum < G->vexnum){
if(G->vertices[index].data.flag==1){
W = G->vertices[index].first;
while(W != NULL){
if(W->adjvex == *Vi){
RemoveArcNode(G,G->vertices[index].data,V);
break;
}
W = W->next;
}
sum++;
}
index++;
}
G->vertices[*Vi].data.flag = 0;
G->vexnum--;
return true;
}
//队列的数据信息
typedef struct LinkNode{
int data;
struct LinkNode *next;
}LinkNode;
//链队列定义
typedef struct{
LinkNode *front,*rear;//*front 队头指针, *rear 队尾指针
}LinkQueue;
//初始化带头结点的队列
int InitLinkQueue(LinkQueue *Q){
(*Q).front = (*Q).rear = (LinkNode *)malloc(sizeof(LinkNode));
if((*Q).front == NULL || (*Q).rear == NULL) return false;
(*Q).front->next = NULL;
return true;
};
//带头结点判断是否为空
int IsEmpty(LinkQueue Q){
if(Q.front == Q.rear) return true;
else return false;
}
//带头结点入队操作
int EnQueue(LinkQueue *Q,int e){
LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
if(p==NULL) return false;//分配内存失败
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
}
//带头结点出队操作
int DeQueue(LinkQueue *Q,int *e){
if(Q->front == Q->rear) return false;//队列为空,出队失败
LinkNode *p = Q->front->next;
*e = p->data;
Q->front->next = p->next;
if(Q->rear == p){ //如果出队的元素是对尾元素,需要特殊处理
Q->rear = Q->front;
}
free(p);//释放片
return true;
}
//拓扑排序 图G,print:表示结果集
boolean TopologicalSort(MGraph *G,char **print){
int *visted = (int *)malloc(sizeof(int)*G->vexnum);//入度记录的数组
*print = (char *)malloc(sizeof(char)*G->vexnum);//返回的拓扑排序序列
//初始化入度记录数组和拓扑排序结果集
for(int i = 0;i < G->vexnum;i++){
visted[i] = 0;
(*print)[i] = '~';
}
//根据图G更新入度记录的数组
for(int i = 0;i < G->vexnum;i++){
ArcNode *node = G->vertices[i].first;
while(node != NULL){
visted[node->adjvex]++;
node = node->next;
}
}
LinkQueue Q;//定义队列
InitLinkQueue(&Q);//初始化队列
int count = 0;//入队列的顶点数目
//入度为0的入队列
for(int i = 0;i < G->vexnum;i++){
if(visted[i] == 0){
EnQueue(&Q,i);
}
}
//循环队列是否为空,寻找其他顶点
while(!IsEmpty(Q)){
int e;//队列的队头元素
DeQueue(&Q,&e);//获取队列队头元素
(*print)[count++] = G->vertices[e].data.v;//获取队头元素的值记入结果集
ArcNode *node = G->vertices[e].first;
while(node != NULL){//寻找队头元素该顶点的边
if(--visted[node->adjvex] == 0){//入度--,如果为0了就加入队列
EnQueue(&Q,node->adjvex); }
node = node->next;
}
}
if(count == G->vexnum){//全部进入队列返回拓扑排序成功,否则返回失败
return true;
}else{
return false;
}
}
//打印拓扑排序的结果集合
void printfTopo(MGraph *G,char *print){
printf("拓扑排序的结果:");
for(int i = 0;i < G->vexnum;i++){
printf("%2c",print[i]);
}
}
//拓扑排序使用
void TopoSort(MGraph *G){
char *print;
boolean flag = TopologicalSort(G,&print);
if(flag){
printfTopo(G,print);
}else{
printf("不是AOV网,拓扑排序失败");
}
}
int main(){
MGraph *G;
MG_Init(&G);
printf("顶点数:%d;边数:%d\n",G->vexnum,G->arcnum);
ElemType V1;
V1.v = 'A';
InsertV(G,V1);
ElemType V2;
V2.v = 'B';
InsertV(G,V2);
ElemType V3;
V3.v = 'C';
InsertV(G,V3);
ElemType V4;
V4.v = 'D';
InsertV(G,V4);
ElemType V5;
V5.v = 'E';
InsertV(G,V5);
ElemType V6;
V6.v = 'F';
InsertV(G,V6);
ElemType V7;
V7.v = 'G';
InsertV(G,V7);
printf("顶点数:%d;边数:%d\n",G->vexnum,G->arcnum);
AddArcNode(G,V1,V2,1);
AddArcNode(G,V1,V6,1);
AddArcNode(G,V2,V3,1);
AddArcNode(G,V3,V7,1);
AddArcNode(G,V4,V2,1);
AddArcNode(G,V4,V5,1);
AddArcNode(G,V5,V6,1);
AddArcNode(G,V6,V3,1);
// AddArcNode(G,V3,V5,1);
printf("顶点数:%d;边数:%d\n",G->vexnum,G->arcnum);
TopoSort(G);
return 0;
}
//结果:
顶点数:0;边数:0
顶点数:7;边数:0
顶点数:7;边数:8
拓扑排序的结果: A D B E F C G

8.13、关键路径、AOE网

在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成该活动的时间),称之为用边表示活动的网络,简称AOE网(Activity On Edge NetWork)

AOE网具有的两个性质:

  • 只有在某个顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始
  • 只有进入的某个顶点的各有向边所代表的活动都已经结束时,该顶点所代表的事件才能发生。另外,有些活动是可以并行进行的。

在AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),表示整个工程的开始;

也仅有一个出度为0的顶点,称为结束顶点(汇点),表示整个工程的结束。

从源点到汇点的有向路径中可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动

关键路径的寻找

posted @   水三丫  阅读(1794)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示