有向无环图
简称DAG图,是用来描述一项工程或系统的进行过程的有效工具。
AOV网:用顶点表示活动,用弧表示活动间的优先关系的有向图称为AOV网,是无权有向无环图
AOE网:用顶点表示事件,弧表示活动权表示活动持续的时间称为AOE网,是带权有向无环图
拓扑排序
拓扑排序就是将AOV网中所有顶点按流程次序排成一个线性序列,该线性序列并不唯一,其过程为:
(1)在有向图中选一个无前驱的顶点且输出它
(2)从图中删除该顶点和并清除以它为前驱的入度
(3)重复一、二步骤,直至不存在无入度为0的顶点
(4)若此时输出的顶点数小于有向图中的顶点数,则说明有向图中存在环,否则输出的顶点序列即为一个拓扑序列。
在实际操作中我们并不一定需要直接对图进行操作,可以借助辅助结构比如栈和队列来实现拓扑排序
实现代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAXSIZE 100
//邻接表
typedef struct ArcNode
{
int adj;
struct ArcNode *next;
} ArcNode;
typedef struct VNode
{
char data;
ArcNode *First;
} VNode, AdjList[MAXSIZE];
typedef struct
{
AdjList ver;
int vexnum, arcnum;
} ALGraph;
ALGraph G;
//栈
typedef struct Stack
{
int *base;
int *top;
int stacksize;
} STACK;
STACK S;
//栈的操作
int InitStack()
{
S.base = malloc(MAXSIZE * sizeof(int));
if (!S.base)
{
return 0;
}
else
{
S.top = S.base;
S.stacksize = MAXSIZE;
return 1;
}
}
int StackEmpty()
{
return S.top == S.base;
}
int Push(int e)
{
if (S.top - S.base == S.stacksize)
{
return 0;
}
else
{
*S.top++ = e;
S.stacksize--;
return 1;
}
}
int Pop()
{
if (S.top == S.base)
{
return 0;
}
else
{
--S.top;
return 1;
}
}
int GetTop()
{
if (S.top != S.base)
{
return *(S.top - 1);
}
}
//创建邻接表表示图
void CreateGraph()
{
ArcNode *p, *q;
int x, y;
printf("Please input the number of vexs:");
scanf("%d", &G.vexnum);
printf("Please input the number of arcs:");
scanf("%d", &G.arcnum);
getchar();
for (int i = 1; i <= G.vexnum; i++)
{
printf("Please input the %dth vex:", i);
scanf("%c", &G.ver[i].data);
getchar();
}
for (int i = 1; i <= G.arcnum; i++)
{
printf("Please input the start:");
scanf("%d", &x);
printf("Please input the end:");
scanf("%d", &y);
if (G.ver[x].First)
{
p = G.ver[x].First;
while (p->next)
{
p = p->next;
}
q = malloc(sizeof(ArcNode));
q->adj = y;
p->next = q;
q->next = NULL;
}
else
{
q = malloc(sizeof(ArcNode));
q->adj = y;
q->next = NULL;
G.ver[x].First = q;
}
}
return;
}
//拓扑排序
bool TopoSort(int topo[])
{
int In[MAXSIZE] = {0};
ArcNode *p;
for (int i = 1; i <= G.vexnum; i++)
{
p = G.ver[i].First;
while (p)
{
In[p->adj]++;
p = p->next;
}
}
for (int i = 1; i <= G.vexnum; i++)
{
if (!In[i])
{
Push(i);
}
}
int s, t=0,cnt = 0;
while (!StackEmpty())
{
s = GetTop();
topo[t++]=s;
Pop();
printf("%c\n", G.ver[s].data);
cnt++;
p = G.ver[s].First;
while (p)
{
In[p->adj]--;
if (!In[p->adj])
{
Push(p->adj);
p = p->next;
}
else
{
p = p->next;
continue;
}
}
}
if (cnt < G.vexnum)
{
return false;
}
else
{
return true;
}
}
int main()
{
InitStack();
CreateGraph();
int topo[MAXSIZE];
if (TopoSort(topo))
{
printf("The graph has no circle!\n");
}
else
{
printf("The graph has circle!\n");
}
return 0;
}
时间复杂度:O(n+e)
关键路径
一个工程在无环的情况下,网只有一个入度为0的点称为源点,只有一个出度为0的点称为汇点。
关键路径就是从源点到汇点带权路径长度最长的路径,也是工程完成的最短时间,关键路径上的活动称为关键活动,关键活动是影响整个工程进度的关键,它们的提前或拖延将将使整个工程提前或拖延。
求解流程:
(1)利用拓扑序列求出每个事件的最早发生时间ve(i)
(2)按逆拓扑序列求出每个事件的最迟发生时间vl(i)
(3)求出每个活动的最早开始时间e(i)
(4)求出每个活动的最晚开始时间l(i)
(5)找出e(i)=l(i)的活动,即为关键活动,其形成的由源点到汇点的每一条路径就是关键路径,关键路径有可能不止一条。
实现代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAXSIZE 100
//邻接表
typedef struct ArcNode
{
int adj;
int weight;
struct ArcNode *next;
} ArcNode;
typedef struct VNode
{
char data;
ArcNode *First;
} VNode, AdjList[MAXSIZE];
typedef struct
{
AdjList ver;
int vexnum, arcnum;
} ALGraph;
ALGraph G;
int topo[MAXSIZE];
//栈
typedef struct Stack
{
int *base;
int *top;
int stacksize;
} STACK;
STACK S;
//栈的操作
int InitStack()
{
S.base = malloc(MAXSIZE * sizeof(int));
if (!S.base)
{
return 0;
}
else
{
S.top = S.base;
S.stacksize = MAXSIZE;
return 1;
}
}
int StackEmpty()
{
return S.top == S.base;
}
int Push(int e)
{
if (S.top - S.base == S.stacksize)
{
return 0;
}
else
{
*S.top++ = e;
S.stacksize--;
return 1;
}
}
int Pop()
{
if (S.top == S.base)
{
return 0;
}
else
{
--S.top;
return 1;
}
}
int GetTop()
{
if (S.top != S.base)
{
return *(S.top - 1);
}
}
//创建邻接表表示图
void CreateGraph()
{
ArcNode *p, *q;
int x, y, z;
printf("Please input the number of vexs:");
scanf("%d", &G.vexnum);
printf("Please input the number of arcs:");
scanf("%d", &G.arcnum);
getchar();
for (int i = 1; i <= G.vexnum; i++)
{
printf("Please input the %dth vex:", i);
scanf("%c", &G.ver[i].data);
getchar();
}
for (int i = 1; i <= G.arcnum; i++)
{
printf("Please input the start:");
scanf("%d", &x);
printf("Please input the end:");
scanf("%d", &y);
printf("Please input the weight:");
scanf("%d", &z);
if (G.ver[x].First)
{
p = G.ver[x].First;
while (p->next)
{
p = p->next;
}
q = malloc(sizeof(ArcNode));
q->adj = y;
q->weight = z;
p->next = q;
q->next = NULL;
}
else
{
q = malloc(sizeof(ArcNode));
q->adj = y;
q->weight = z;
q->next = NULL;
G.ver[x].First = q;
}
}
return;
}
//拓扑排序
bool TopoSort()
{
int In[MAXSIZE] = {0};
ArcNode *p;
for (int i = 1; i <= G.vexnum; i++)
{
p = G.ver[i].First;
while (p)
{
In[p->adj]++;
p = p->next;
}
}
for (int i = 1; i <= G.vexnum; i++)
{
if (!In[i])
{
Push(i);
}
}
int s, t = 1, cnt = 0;
printf("The topo sort is:\n");
while (!StackEmpty())
{
s = GetTop();
topo[t++] = s;
Pop();
printf("%c\n", G.ver[s].data);
cnt++;
p = G.ver[s].First;
while (p)
{
In[p->adj]--;
if (!In[p->adj])
{
Push(p->adj);
p = p->next;
}
else
{
p = p->next;
continue;
}
}
}
if (cnt < G.vexnum)
{
return false;
}
else
{
return true;
}
}
//关键路径
void CriticalPath()
{
ArcNode *p;
TopoSort();
int ve[MAXSIZE], vl[MAXSIZE], e, j, l, k;
for (int i = 1; i <= G.vexnum; i++)
{
ve[i] = 0;
}
for (int i = 1; i <= G.vexnum; i++)
{
k = topo[i];
p = G.ver[k].First;
while (p)
{
j = p->adj;
if (ve[j] < ve[k] + p->weight)
{
ve[j] = ve[k] + p->weight;
}
p = p->next;
}
}
for (int i = 1; i <= G.vexnum; i++)
{
vl[i] = ve[G.vexnum];
}
for (int i = G.vexnum; i >= 1; i--)
{
k = topo[i];
p = G.ver[k].First;
while (p)
{
j = p->adj;
if (vl[k] > vl[j] - p->weight)
{
vl[k] = vl[j] - p->weight;
}
p = p->next;
}
}
printf("The CriticalPath is:\n");
for (int i = 1; i <= G.vexnum; i++)
{
p = G.ver[i].First;
while (p)
{
j = p->adj;
e = ve[i];
l = vl[j] - p->weight;
if (e == l)
{
printf("%c->%c\n", G.ver[i].data, G.ver[j].data);
}
p = p->next;
}
}
return;
}
int main()
{
InitStack();
CreateGraph();
CriticalPath();
return 0;
}
时间复杂度:O(n+e)
实践证明,用AOE网对于估算工程完成的时间是十分有用的。我们再实施工程时对关键路径上的关键活动要十分关注,因为关键路径决定了工期的长短,与此同时,我们还可以对非关键路径上的活动进行合理安排。若网中有多条关键路径,那么单一提高一条关键路径上的是速度还不能导致整个工期缩短,必须同时提高所有关键路径上的活动速度才能达到期望值。