拓扑排序
百度百科上对拓扑排序的解释:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。
图的拓扑排序是解决工程能否顺序执行。
在图结构中,只有有向无环图才有拓扑排序,其中每次能排在线性序列上的都是图中入度为 0 的结点。如果开始有多个入度为 0 的结点,可以随便安排哪一个,无硬性要求。
先来看看如下有向无环图
按照每次选择出入度为0的结点的准则,进行排序
对于图中的结点输出,需要辅助的栈结构或者是队列结构,将入度为0的结点都存入栈或者队列中,以此作为条件来进行深度遍历图,直到栈或者队列为空。
代码
#include <stdio.h>
#include <stdlib.h>
typedef char VertexType;
#define MAXVEX 100 //最大顶点数 Maximum vertex
typedef struct EdgeNode //边表结点
{
int adjvex; //邻接顶点域
int weight; //权值
struct EdgeNode *next;
}EdgeNode;
typedef struct VertexNode //顶点表结点
{
int in; //顶点入度
VertexType data; //存储顶点信息
EdgeNode *firstedge; //边表的头指针
}VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjlist;
int numVertexes;
int numEdges;
}GraphAdjList;
void create_Graph(GraphAdjList *G)
{
int i, j;
EdgeNode *e;
printf("Please enter the number of vertexes and edges : ");
scanf("%d %d", &G->numVertexes, &G->numEdges);
for (i = 0; i < G->numVertexes; i++)
{
getchar();
scanf("%d", &G->adjlist[i].in);
getchar();
scanf("%c", &G->adjlist[i].data);
// scanf("%d %c", &G->adjlist[i].in, &G->adjlist[i].data);
G->adjlist[i].firstedge = NULL;
}
for (int k = 0; k < G->numEdges; k++)
{
printf("Please enter vertex serial number on the edge like (vi, vj) :");
scanf("%d %d", &i, &j);
e = (EdgeNode *)malloc(sizeof(EdgeNode));
e->adjvex = j;
e->next = G->adjlist[i].firstedge;
G->adjlist[i].firstedge = e;
}
}
bool TopologicalSort(GraphAdjList G)
{
EdgeNode *e;
int k, gettop;
int top = -1; //栈的标记
int Count = 0; //统计输出顶点的个数
int *Stack; //声明栈
Stack = (int *)malloc(sizeof(int) * G.numVertexes); //创建栈
for (int i = 0; i < G.numVertexes; i++)
{
if (G.adjlist[i].in == 0)
{
//找到入度为0的顶点存入栈中
Stack[++top] = i;
}
}
while(top != -1)
{
gettop = Stack[top--]; //出栈
Count++;
if (Count >= G.numVertexes)
{
printf("%c", G.adjlist[gettop].data);
}
else
{
printf("%c -> ", G.adjlist[gettop].data);
}
for (e = G.adjlist[gettop].firstedge; e; e = e->next)
{
k = e->adjvex;
if (!(--G.adjlist[k].in))
{
Stack[++top] = k;
}
}
}
printf("\n");
if (Count < G.numVertexes)
{ //不满足顶点数则存在环
return false;
}
else
{
return true;
}
}
int main()
{
GraphAdjList G;
create_Graph(&G);
//拓扑排序算法
if (!TopologicalSort(G))
{
printf("该AOV网存在环!\n");
}
else
{
printf("该AOV网不存在环!\n");
}
return 0;
}
关键路径
先来看看下面的图
由上面的AOE网图分析出,组装一辆车时需要将很多部件组装在一起,若将这些一步一步进行那就是工厂要倒闭了,可以同时进行,并且这些不同部件耗费的时间长短不一,若要给验收人一个等待验收的时间,那就是组装这些部件中用时最长的作为等待验收的时间(选择时间长一点的),这就是所谓的关键时间了;对于带权的有向无环网图来说,关键路径就麻烦点。给出书中的AOE网图
图中顶点表示事件,弧表示活动。
事件的最早发生时间,比如 v2 的最早发生时间(也就是从 V2 开始)是 4
事件的最晚发生时间,v2 的最晚发生时间是在 V5 或者 V3 的时间上减去与 V2 构成的权值,其实这是关键路径的特殊之处(早和晚的都相等)。
那么活动的最早开工时间和事件的最早开工事件是一样的;活动的最晚开工时间也会和事件的发生时间相似;只是活动的最早和最晚是用来判断关键路径的。
该AOE网图的关键路径如下
代码
#include <stdio.h>
#include <stdlib.h>
typedef char VertexType;
#define MAXVEX 100
int *etv, *ltv; //事件最早发生时间和最晚发生时间
int *Stack2; //用于存储拓扑排序列的栈
int top2;
typedef struct EdgeNode
{
int adjvex;
int weight;
struct EdgeNode *next;
}EdgeNode;
typedef struct VertexNode
{
int in;
VertexType data;
EdgeNode *firstedge;
}VertexNode, AdjList[MAXVEX];
typedef struct
{
AdjList adjList;
int numVertexes;
int numEdges;
}GraphAdjList;
void creat_Graph(GraphAdjList *G)
{
int i, j, numWeight;
EdgeNode *e;
printf("Please enter the number of vertexes and edges : ");
scanf("%d %d", &G->numVertexes, &G->numEdges);
for (i = 0; i < G->numVertexes; i++)
{
getchar();
scanf("%d", &G->adjList[i].in);
getchar();
scanf("%c", &G->adjList[i].data);
// scanf("%d %c", &G->adjlist[i].in, &G->adjlist[i].data);
G->adjList[i].firstedge = NULL;
}
for (int k = 0; k < G->numEdges; k++)
{
printf("Please enter vertex serial number on the edge like (vi, vj) and the weight :");
scanf("%d %d %d", &i, &j, &numWeight);
e = (EdgeNode *)malloc(sizeof(EdgeNode));
e->adjvex = j;
e->weight = numWeight;
e->next = G->adjList[i].firstedge;
G->adjList[i].firstedge = e;
}
}
void TopologicalSort(GraphAdjList G)
{
EdgeNode *e;
int k, gettop;
int top = -1;
int Count = 0; //统计输出顶点的个数
int *Stack;
Stack = (int *)malloc(sizeof(int) * G.numVertexes);
for (int i = 0; i < G.numVertexes; i++)
{
if (0 == G.adjList[i].in)
{
Stack[++top] = i;
}
}
top2 = -1;
//事件最早发生时间
etv = (int *)malloc(sizeof(int) * G.numVertexes);
for(int i = 0; i < G.numVertexes; i++)
{
//初始化
etv[i] = 0;
}
Stack2 = (int *)malloc(sizeof(int) * G.numVertexes);
while(top != -1)
{
gettop = Stack[top--];
Count++;
Stack2[++top2] = gettop;
for (e = G.adjList[gettop].firstedge; e; e = e->next)
{
k = e->adjvex;
if (!(--G.adjList[k].in))
{
Stack[++top] = k;
}
//求各顶点事件最早发生时间值
if ((etv[gettop] + e->weight) > etv[k])
{
etv[k] = etv[gettop] + e->weight;
}
}
}
}
void CriticalPath(GraphAdjList G)
{
EdgeNode *e;
int gettop, k;
TopologicalSort(G);
//事件最晚发生的时间
ltv = (int *)malloc(sizeof(int) * G.numVertexes);
for (int i = 0; i < G.numVertexes; i++)
{
ltv[i] = etv[G.numVertexes - 1];
}
while (top2 != -1)
{
gettop = Stack2[top2--];
for (e = G.adjList[gettop].firstedge; e; e = e->next)
{
//求各顶点事件的最迟发生时间ltv值
k = e->adjvex;
if (ltv[k] - e->weight < ltv[gettop])
{
//求各顶点事件最晚发生时间
ltv[gettop] = ltv[k] - e->weight;
}
}
}
int ete, lte; //活动最早发生时间和最晚发生时间
for (int j = 0; j < G.numVertexes; j++)
{
for (e = G.adjList[j].firstedge; e; e = e->next)
{
k = e->adjvex;
//活动最早发生时间
ete = etv[j];
//活动最晚发生时间
lte = ltv[k] - e->weight;
if (ete == lte)
{
printf("<%c, %c> length: %d ,", G.adjList[j].data, G.adjList[k].data, e->weight);
}
}
}
}
int main()
{
GraphAdjList G;
creat_Graph(&G);
CriticalPath(G);
return 0;
}