有向图的关键路径

578 阅读5分钟
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXEDGE 30
#define MAXVEX 30
#define INFINITYC 65535

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */

/* 邻接矩阵结构 */
typedef struct
{
    int vexs[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;

/* 邻接表结构****************** */
//边表结点
typedef struct EdgeNode
{
    //邻接点域,存储该顶点对应的下标
    int adjvex;
    //用于存储权值,对于非网图可以不需要
    int weight;
    //链域,指向下一个邻接点
    struct EdgeNode *next;
}EdgeNode;

//顶点表结点
typedef struct VertexNode
{
    //顶点入度
    int in;
    //顶点域,存储顶点信息
    int data;
    //边表头指针
    EdgeNode *firstedge;
}VertexNode, AdjList[MAXVEX];

typedef struct
{
    AdjList adjList;
    //图中当前顶点数和边数
    int numVertexes,numEdges;
}graphAdjList,*GraphAdjList;

/* 关于AOE网图的存储代码段-Begin */
//1.完成AOE网图关于邻接矩阵的存储
void CreateMGraph(MGraph *G)/* 构件图 */
{
    int i, j;
    /* printf("请输入边数和顶点数:"); */
    G->numEdges=13;
    G->numVertexes=10;

    for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
    {
        G->vexs[i]=i;
    }

    for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            if (i==j)
                G->arc[i][j]=0;
            else
                G->arc[i][j]=INFINITYC;
        }
    }

    G->arc[0][1]=3;
    G->arc[0][2]=4;
    G->arc[1][3]=5;
    G->arc[1][4]=6;
    G->arc[2][3]=8;
    G->arc[2][5]=7;
    G->arc[3][4]=3;
    G->arc[4][6]=9;
    G->arc[4][7]=4;
    G->arc[5][7]=6;
    G->arc[6][9]=2;
    G->arc[7][8]=5;
    G->arc[8][9]=3;

}
//2.将邻近矩阵转化成邻接表
void CreateALGraph(MGraph G,GraphAdjList *GL){

    int i,j;
    EdgeNode *e;

    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));

    (*GL)->numVertexes=G.numVertexes;
    (*GL)->numEdges=G.numEdges;

    //读入顶点信息,建立顶点表
    for(i= 0;i <G.numVertexes;i++)
    {
        (*GL)->adjList[i].in=0;
        (*GL)->adjList[i].data=G.vexs[i];
        //将边表置为空表
        (*GL)->adjList[i].firstedge=NULL;
    }

    //建立边表
    for(i=0;i<G.numVertexes;i++)
    {
        for(j=0;j<G.numVertexes;j++)
        {
            if (G.arc[i][j]!=0 && G.arc[i][j]<INFINITYC)
            {
                e=(EdgeNode *)malloc(sizeof(EdgeNode));
                //邻接序号为j
                e->adjvex=j;
                e->weight=G.arc[i][j];
                //将当前顶点上的指向的结点指针赋值给e
                e->next=(*GL)->adjList[i].firstedge;
                //将当前顶点的指针指向e
                (*GL)->adjList[i].firstedge=e;
                (*GL)->adjList[j].in++;

            }
        }
    }
}

//
int *etv,*ltv;//事件最早发生的时间和最晚发生的时间
int *stack2;//存储拓扑排序的序列的栈
int top2;//栈stack2的栈顶指针

//拓扑排序
Status TopologicalSort(GraphAdjList GL){
    //1.创建一个栈,保存入度为0的顶点下标
    int *stack = (int *)malloc(GL->numVertexes*sizeof(int));
    int top = 0;//栈stack的栈顶指针
    //2.将入度为0的顶点下标入栈
    for (int i=0; i<GL->numVertexes; i++) {
        if (GL->adjList[i].in == 0) {
            stack[++top] = i;
        }

    }
    //3.创建一个栈,保存拓扑排序序列
    stack2 = (int *)malloc(GL->numVertexes*sizeof(int));
    top2 = 0;//stack2的栈顶指针
    //4.创建一个数组,保存顶点事件的最早发生时间
    etv = malloc(GL->numVertexes*sizeof(int));
    //5.初始化etv
    for (int i=0; i<GL->numVertexes; i++) {
        etv[i] = 0;
    }
   printf("TopologicSort:\t");
    int gettop;
    int count = 0;//计数
    EdgeNode *e;
    int k;
    //6.将stack出栈,并将保存的对应下标的顶点的入度-1,如果-1后入度为0,则入栈stack
    while (top !=0 ) {
        //拿到栈顶元素
        gettop = stack[top--];
        printf("%d ->",GL->adjList[gettop].data);
        count++;
        //将出栈的顶点下标入栈
        stack2[++top2] = gettop;
        //遍历与顶点gettop相连的所有顶点,将其入度-1
        for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
            //相连的顶点下标
            k = e->adjvex;
            if ( (--GL->adjList[k].in) == 0 ) {
                //入度为0的顶点下标入栈
                stack[++top] = k;
            }
            
            //求各顶点事件的最早发生时间etv
            if (etv[gettop]+e->weight > etv[k]) {
                etv[k] = etv[gettop] + e->weight;
            }
        }

    }
    printf("\n");
    
    for (int i = 0; i<GL->numVertexes; i++) {
        printf("etv[%d] = %d\n",i,etv[i]);
    }
    printf("\n");
    
    if (count<GL->numVertexes) {
        return ERROR;
    }else{
        return OK;
    }
     
}

//求关键路径,GL为有向网,则输出G的各项关键活动
void CriticalPath(GraphAdjList GL){
    //1.对GL进行拓扑排序,得到事件的最早发生时间etv,拓扑序列stack2
    TopologicalSort(GL);
    //2.定义事件的最晚发生时间,并初始化(etv的最后一个数据为最后一个事件发生的最早时间,以它来初始化ltv数组)
    ltv = (int *)malloc(sizeof(int)*GL->numVertexes);
    for (int i=0; i<GL->numVertexes; i++) {
        //
        ltv[i] = etv[GL->numVertexes -1];
    }
    
    int k;
    int gettop;
    EdgeNode *e;
    //3.将事件栈stack2出栈
    while (top2 != 0) {
        //3.1将拓扑排序序列出栈,得到事件的下标
        gettop = stack2[top2--];
        //3.2找到与栈顶元素相连的点
        for (e = GL->adjList[gettop].firstedge; e; e = e->next) {
            k = e->adjvex;
            //更新事件的最晚发生时间ltv
            if (ltv[k]-e->weight < ltv[gettop]) {
                ltv[gettop] = ltv[k] - e->weight;
            }

        }
    }
    //打印ltv数组
    printf("ltv:\n");
    for (int i=0; i<GL->numVertexes; i++) {
        printf("ltv[%d] = %d\n",i,ltv[i]);
    }
    printf("\n");
    
    int ete,lte;
    //4.求ete,lte,如果ete和lte相等,则是关键活动
    for (int j =0 ; j<GL->numVertexes; j++) {
        for (e = GL->adjList[j].firstedge; e; e = e->next) {
            k = e->adjvex;
            //ete就是表示活动<Vk,Vj>的最早开工时间,是针对这条弧来说的,而这条弧的弧尾顶点Vk,的事件发生了,它才可以发生,因此ete = etv[k];
            ete = etv[j];
            //lte表示活动<Vk,Vj>的最晚开工时间,但此活动再晚也不能等Vj事件发生才开始,而是必须在Vj事件之前发生,lte = ltv[k] - len<Vk,Vj>
            
            lte = ltv[k] - e->weight;
            //如果ete == lte则,该活动为关键活动
            if (ete == lte) {
                printf("<%d,%d> weight:%d\n",GL->adjList[j].data,GL->adjList[k].data,e->weight);
            }

        }

    }


}

int main(int argc, const char * argv[]) {
    MGraph G;
    GraphAdjList GL;
    CreateMGraph(&G);
    CreateALGraph(G, &GL);
    //拓扑排序
//    TopologicalSort(GL);
    //关键路径
    CriticalPath(GL);
    return 0;
}