关键路径(C语言 邻接表)

综述

一共四步

1、根据拓扑排序求ve

2、根据逆拓扑排序求vl

3、由ve求e,由vl求l

4、输出关键活动、权值

***************************************************************************************************************************

其中:

顶点对应事件发生,最早ve,最晚用vl

边对应活动开始,最早用e,最晚用l

求法

1、ve:ve[i] = max{ve[前]+边权}

              ve[0]=0

2、vl:vl[i] = min{vl[后]-边权}

             vl[最后]=ve[最后]

3、e:ve[前]

4、l:vl[后]-边权

***************************************************************************************************************************

关键路径

若额e[i] == l[i],则为关键活动

数据结构

//增加id用于标识边,与e和l数组下标对应
//增加data记录边权值
typedef struct Side//边
{
    int toVertex;//边指向的点
    int data;
    int id;
    struct side *next;
}Side,*sLink;
typedef struct Vertex//顶点
{
    int data;
    sLink first;//第一个边
}Vertex,AdjList[20];
typedef struct Graph//图
{
    AdjList adj;//顶点数组,注意不是指针,用.不用->
    int n,v;//顶点数,边数
}Graph,*gLink;

创建

//新增边的权值与id
void createGraph(gLink g)
{
    int n,v,data;
    printf("请输入顶点数与边数");
    scanf("%d %d",&n,&v);
    g->n = n;
    g->v = v;
    int i;
    for(i=0;iadj[i].data = data;
        g->adj[i].first = NULL;
    }
    printf("请输入边信息");
    int v1,v2,da;
    for(i=0;itoVertex = v2;
        s->next = g->adj[v1].first;
        g->adj[v1].first = s;
        s->data = da;
        s->id = i;
    }
}

入度

void inDegree(gLink g,int *a)
{
    int i;
    for(i=0;in;i++)
    {
        a[i]=0;
    }
    for(i=0;in;i++)
    {
        sLink s = g->adj[i].first;
        while(s)
        {
            a[s->toVertex]++;
            s = s->next;
        }
    }
}

全局变量

//全局变量
int vers[15];//栈,装入拓扑排序,用于逆拓扑排序
int top = -1;//栈指针
//顶点-->事件发生:最早ve,最晚vl
//边-->活动:最早e,最晚l
int ve[15]={0},vl[15]={0},e[15]={0},l[15]={0};

拓扑排序求ve

//拓扑排序求ve
void tuopu(gLink g)
{
    int queue[10];
    int front=0, rear=0;
    int a[g->n];
    inDegree(g,a);
    int i;
    for(i=0;in;i++)
    {
        if(!a[i])
        {
            queue[rear++] = i;
        }
    }
    while(front!=rear)
    {
        int v = queue[front++];
        vers[++top] = v;//进栈
        sLink s = g->adj[v].first;
        while(s)
        {
            if(!--a[s->toVertex])
            {
                queue[rear++] = s->toVertex;
            }
            if(ve[v]+s->data>ve[s->toVertex]) ve[s->toVertex] = ve[v]+s->data;//为每一个后继顶点赋值ve
            s = s->next;
        }
    }
}

关键路径

//关键路径
void path(gLink g)
{
    //根据逆拓扑求vl
    int i;
    int v = vers[top--];
    vl[v] = ve[v];
    while(top!=-1)
    {
        v = vers[top--];//出栈
        sLink s = g->adj[v].first;
        vl[v]=vl[s->toVertex];
        while(s)
        {
            if(vl[s->toVertex]-s->datatoVertex]-s->data;//为每一个前驱顶点赋值vl
            s = s->next;
        }
    }
    //求e和l
    //从顶点遍历,为每一个边确定e和l
    for(i=0;in;i++)
    {
        sLink s = g->adj[i].first;
        while(s)
        {
            e[s->id] = ve[i];//当前边e等于顶点ve
            l[s->id] = vl[s->toVertex] - s->data;//当前边l等于指向顶点vl减去边权值
            s = s->next;
        }
    }
    //求关键路径
    //输出关键活动和边的头顶点尾顶点
    //e=l的边为关键活动
    int j;
    for(i=0;iv;i++)
    {
        if(l[i]==e[i])
        {
            printf("a%-5d",i);
            //遍历图,找到id为这个的边
            for(j=0;jn;j++)
            {
                sLink s = g->adj[j].first;
                while(s)
                {
                    if(s->id==i)
                    {
                        printf("v%d-->v%d",j,s->toVertex);
                        break;
                    }
                    s=s->next;
                }
            }
            printf("\n");
        }
    }
    //求总权值
    //由于关键路径不一定只有一条
    //所以从顶点开始,找到关键活动则跳到指向的下一个顶点,这样可以只得到一条关键路径
    int quan = 0;
    sLink s = g->adj[0].first;
    while(s)
    {
        if(e[s->id]==l[s->id])
        {
            quan += s->data;
            s = g->adj[s->toVertex].first;
        }else
        {
            s = s->next;
        }
    }
    printf("权值为:%d",quan);
}

主函数

int main()
{
    gLink g = (gLink)malloc(sizeof(Graph));
    createGraph(g);
    tuopu(g);
    path(g);
    return 0;
}

你可能感兴趣的:(数据结构)