关键路径算法

关键路径

介绍:

对于关键路径的详细介绍看王道P234页,对于里面事件最早/最晚发生时间,活动的最早/最晚开始时间的概念要清晰,计算要清晰,能够手动模拟关键路径的计算

算法

//使用邻接表来创建AOE网
int CreatUN_AOE(ALGraph* G){
        ArcNode* r[MAX_VERTEX_NUM];     //用作访问标记,用来定位
        ArcNode* p;                     //需要插入的结点
        printf("Please input the num of vex and arc:");
        scanf("%d,%d",&(G->vexnum),&(G->arcnum));
        getchar();

        //初始化数据
        printf("Please input the data of vex:\n");
        for (int i = 0; i < G->vexnum; ++i) {
            scanf("%d",&(G->vertices[i].data));
            getchar();
            G->vertices[i].firstarc = NULL;
            r[i] = NULL;
        }

        int v1,v2,info;
        //读取各边,开始制作邻接表
        for (int i = 0; i < G->arcnum; ++i) {
            printf("Please input the trail , head and info:");
            scanf("%d,%d,%d",&v1,&v2,&info);
            getchar();

            //这里的头和尾指的是,弧头的顶点和弧尾的顶点
            int trail = LocateVex_AL(*G,v1);
            int head = LocateVex_AL(*G,v2);

            //判断图中是否含有顶点
            if (head==-1||trail==-1){
                return 0;
            }
            //制作结点
            p = (ArcNode*)malloc(sizeof (ArcNode));
            if (!p){
                exit(0);    //分配内存失败
            }
            p->adjvex = head;
            p->info = info;
            p->nextarc = NULL;
            if (r[trail] == NULL){
                G->vertices[trail].firstarc = p;
            } else{
                r[trail]->nextarc = p;
            }
            r[trail] = p;
        }
        return 1;
}

void OutPutAllGraph_AOE(ALGraph G){
    ArcNode* p;
    for (int i = 0; i < G.vexnum; ++i) {
        printf("%d->",G.vertices[i].data);
        p = G.vertices[i].firstarc;
        while (p){
            printf("%d|%d\t",p->adjvex,p->info);
            p = p->nextarc;
        }
        printf("\n");
    }
}

//初始化栈操作
#define MaxSize 50      //定义栈的深度
typedef struct {
    int data[MaxSize];      //栈数组
    int top;                //栈头
}SqStack;

//初始化
void InitStack(SqStack* S){
    S->top = -1;        //初始化栈顶指针
}

//判断栈是否为空
bool StackEmpty(SqStack S){
    if (S.top == -1){
        return true;
    } else{
        return false;
    }
}

//进栈
bool Push(SqStack* S,int x){
    if (S->top == MaxSize-1){       //栈满
        return false;
    }
    S->data[++S->top] = x;
    return true;
}

//出栈
bool Pop(SqStack* S,int* x){
    if (S->top == -1){
        return false;       //栈空
    }
    *x = S->data[S->top--];
    return true;
}

//读栈顶元素
bool GetTop(SqStack S,int* x){
    if (S.top == -1){
        return false;
    }
    *x = S.data[S.top];
    return true;
}

//获取所有结点的入度情况
void FindInDegree(ALGraph G,int indegree[MAX_VERTEX_NUM]){
    ArcNode* p;
    for (int i = 0; i < G.vexnum; ++i) {
        indegree[i] = 0;        //初始化数组全部为0
    }
    //遍历邻接表,计算所有顶点的入度情况
    for (int i = 0; i < G.vexnum; ++i) {
        p = G.vertices[i].firstarc;         //指向当前顶点的第一个连接顶点
        while (p){
            indegree[p->adjvex]++;          //度数加一
            p = p->nextarc;
        }
    }
}

//定义全局变量
SqStack T;
int ve[MAX_VERTEX_NUM];     //各时间的最早开始时间
int vl[MAX_VERTEX_NUM];     //各时间的最晚开始时间

/**
 * AOE关键路径的函数列表,该函数的主要目的是获取最早开始时间ve和逆拓扑排序,逆拓扑用栈来存储,
 * 用来计算最晚开始时间vl
 * @param G
 * @param T
 * @return
 */
int TopologicalSort_AOE(ALGraph G){
    int indegree[G.vexnum];     //统计顶点的入度
    FindInDegree(G,indegree);
    ArcNode* p;

    //建立栈结构,用来拓扑排序
    SqStack S;
    InitStack(&S);
    InitStack(&T);

    //查找度为0的顶点,并作为将其压入栈中,作为起始点
    for (int i = 0; i < G.vexnum; ++i) {
        if (indegree[i]==0){
            Push(&S,i);     //压入的时顶点在顶点数组中的下标
        }
        ve[i] = 0;          //初始化最早时间
    }

    int count = 0;

    while (!StackEmpty(S)){
        int index;
        Pop(&S,&index);
        Push(&T,index);      //用来计算最晚开始时间,所以需要先压入栈
        count++;

        //遍历每个结点,获取拓扑排序
        for (p = G.vertices[index].firstarc;p;p = p->nextarc) {
            int k = p->adjvex;      //顶点所在的下标
            //将顶点的入读减一
            indegree[k]--;
            if (indegree[k]==0){        //入度减一之后如果为0,则押入到栈中
                Push(&S,k);
            }

            //计算最早开始时间,如果边的源点的最长路径加上边上的权值,必要汇点的最长路径要长,则覆盖ve数组中对应的位置
            //那么最终结束的时候,ve数组中存储的就是各顶点的最长路径长度
            if (ve[index]+p->info > ve[k]){
                ve[k] = ve[index]+p->info;
            }
        }
    }
    if (count < G.vexnum){
        return false;           //证明有环
    } else{
        return true;
    }
}

/**
 * 主要是计算事件的最晚发生时间,活动的最早和最晚开始时间,然后活动的最早和最晚开始时间相减即可得到最终的关键活动
 * 关键活动所组成的路径即为关键路径
 * @param G
 */
void CriticalPath(ALGraph G){
    //如果拓扑排序失败则证明有环
    if (!TopologicalSort_AOE(G)){
        return;
    }

    //初始化最晚开始时间
    for (int i = 0; i < G.vexnum; ++i) {
        vl[i] = ve[G.vexnum-1];     //使最晚开始时间全部为最中汇点的最早开始时间
    }

    //生成事件最晚发生时间
    int start,end;      //start表示起始点,end表示指向点
    ArcNode* p;
    while (!StackEmpty(T)){
        Pop(&T,&start);
        for (p = G.vertices[start].firstarc;p;p=p->nextarc) {
            end = p->adjvex;      //start点指向的点
            //出事情况下,vl数组中每个单元都是整个图的最终汇点的ve,如果每个边的汇点减去边的权值比源点小,就保存小的
            if (vl[end]-p->info < vl[start]){
                vl[start] = vl[end] - p->info;
            }
        }
    }

    //循环求各边的最早开时间和最晚开始时间
    int k;
    for (int i = 0; i < G.vexnum; ++i) {
        for (ArcNode* t = G.vertices[i].firstarc;t;t=t->nextarc) {
            k = t->adjvex;
            //各活动的最早开时间与各顶点的最早开始时间一样
            int e = ve[i];
            //各活动的最晚开始时间等于各顶点的最晚开始时间减去权值
            int l = vl[k] - t->info;
            //判断是否相等,若相等则表明为关键活动,用*表示
            char tag = (e==l)?'*':' ';
            //分别表示线段的起始点,终点,边上的权值,边上活动的最早开始时间,最晚开始时间,是否是关键活动
            printf("%d %d %d %d %d %c\n",G.vertices[i].data,G.vertices[k].data,t->info,e,l,tag);
        }
    }

}

使用邻接表来表示图

关键路径算法

关键路径算法

上一篇:Spring 属性注入方式


下一篇:redis使用scan实现keys*