zoukankan      html  css  js  c++  java
  • 图的一些总结

    1.图的定义
        1)线性表我们把数据元素叫做元素,树中将数据元素叫结点,在图中数据元素,我们则称之为顶点。
        2)线性表中可以没有数据元素,称为空表。树中可以没有结点,叫做空树。再图结构中,不允许没有顶点。
        3)线性表中,相邻的数据元素之间具有线性关系,树结构中,相邻两层的结点具有层次关系,而图中两个顶点之间都可能有关系,顶点
          之间的逻辑关系来表示,边集可以是空的。
    2.各种图定义
        无向边:若顶点v1到v1之间的边没有方向,则称这条边为无向边,用无序偶对来表示。如果图中任意两个顶点之间的边都是无向边,则称为
        无向图。
        有向边:若从顶点vi到vj的边有方向,则称这条边为有向边,也称为弧。
        如果图中任意两个顶点之间的边都是有向边,则这个图为有向图。
        *看清楚,有向边用小括号"()"表示,而有向边则是用尖括号"<>"表示。
        入度,出度
        连通图,强连通
        图按照有无方向分为无向图和有向图。无向图由顶点和边构成,有向图由顶点和弧构成,弧有弧头和弧尾之分。
        图按照边或弧的多少分为稀疏图和稠密图。如果任意两个顶点之间都存在边叫完全图。有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。
        图中顶点之间有邻接点。无向图顶点的边数叫做度,有向图顶点分为出度和入度。
        图上的边或弧上带权则称为网。
        图中顶点间存在路径,两顶点存在路径则说明是连通,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是
        连通图,有向则称为强连通。
        无向图中连通且n个顶点n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向图。一个有向图由若干棵有向树构成生成森林。
    2.图的存储结构
        1)邻接矩阵
            考虑到图是由顶点和边或弧两部分组成,合在一起比较困难,那就很自然考虑到分两个结构分别存储。顶点不分大小、主次,需要一个二维数组来存储。
        
            图的邻接矩阵存储方式是两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。
            无向图是的边数组是一个对称矩阵。
            对称矩阵
                即从矩阵的左上角到右下角的主对角轴,右上角的元与左下角相对应的元全部都是相等的。
        代码实现:
            typedef char VertexType;/* 顶点类型由用户自定义 */
            typedef int EdgeType;  /* 边上的权值类型应由用户定义 */
            #define MAXVEX  100 /* 最大顶点数,应由用户定义*/
            #define INFINITY 65535 /* 用65535代表无穷*/
            typedef struct
            {
                VertexType vexs[MAXVEX]; /*顶点表*/
                EdgeType  arc[MAXVEX][MAXVEX];/*邻接矩阵,可看做边表 */
                int numVertexes, numEdges;/* 图中当前的 顶点数 和 边数 */
            }MGraph;
        有了这个结构定义,我们构造一个图,其实就是给顶点表和边表输入数据的过程。我们看看无向图的创建代码。
            void CreateMGraph(MGraph  *G)
            {
                int i,j,k,w;
                printf("输入顶点数和边数: ");
                scanf("%d,%d",&G->numVertexes,&G->numEdges);  /* 输入顶点数和边数 */
                for(i=0;i<G->numVertexes;i++)/* 读入顶点信息,建立顶点表*/
                    scanf("%c",&G->vexs[i]);
                for(i=0;i<G->numVertexes;i++)
                    for(j=0;j<G->numVertexes;j++)
                        G->arc[i][j] = INFINITY;/*邻接矩阵初始化*/
                for(k=0;k<G->numEdges;k++)  /**  读入numEdges条边,建立邻接矩阵**/
                {
                    printf("输入边(vi,vj)上的下标i,下标j和权w: ");
                    scanf("%d,%d,%d",&i,&j,&w);/*  输入边(vi,vj)上的权w */
                    G->arc[i][j] = w;
                    G->arc[j][i] = G->arc[i][j];/* 因为是无向图,矩阵对称 */
                }
                
            }
        从代码也可以得到,n个顶点和e条边的无向网图的创建,时间复杂度为O(n+n*n+e),其中对邻接矩阵的初始化耗费了O(n*n)的时间。
        2)邻接表
            邻接矩阵是不错的一种图存储结构,但是我们发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大的浪费
        数组与链表相结合的存储方法称为链接表。
        邻接表的处理方法:
            1.图中顶点用一个一维数组存储。
            2.图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储。
        定义结构体:
            typedef char VertexType;  /* 顶点类型应由用户定义*/
            typedef int EdgeType;  /* 边上的权值类型应该由用户定义*/
            
            typedef struct EdageNode   /* 边表结点 */
            {
                int adjvex; /* 邻边点域,存储该结点对应的下标*/
                EdgeType weight;/* 用于存储权值,对于非网图可以不需要*/
                struct EdgeNode *next;/* 链域,指向下一个邻接点 */
            }EdgeNode;
            
            typedef struct VertexNode  /* 顶点表结点 */
            {
                VertexType data;/*  顶点域,存储顶点信息 */
                EdgeNode *firstedge; /* 边表头指针 */
            }VertexNode,AdjList[MAXVEX];

            typedef struct  
            {
                AdjList adjList;
                int numVertexes,numEdges;   /* 图中当前顶点数和边数  */
            }GraphAdjList;
        无向图的邻接表创建代码如下:(这里没有搞明白,研究一下头插法)
            void CreateALGraph(GraphAdjList *G)
            {
                int i,j,k;
                EdgeNode *e;
                printf("输入顶点数和边数: ");
                scanf("%d,%d",&G->numVertexes,&G->numEdges);/* 输入顶点数和边数 */
                for(i=0;i<G->numVertexes;i++)  /* 读入顶点信息,建立顶点表 */
                {
                    scanf(&G->adjList[i].data);  /* 输入顶点信息  */
                    G->adjList[i].firstedge = NULL;   /* 将边表置为空表 */
                }
                for(k=0;k<G->numEdges;k++)   /* 建立边表*/
                {
                    printf("输入边(vi,vj)上的顶点序号: ");
                    scanf("%d,%d",&i,&j);   /* 输入边(vi,vj)上的顶点序号 */
                    e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间 */
                                        /* 生成边表结点 */
                    e->adjvex = j; /* 邻接序号为j */
                    e->next = G->adjList[i].firstedge; /* 将e指针指向当前顶点指向的结点 */
                    G->adjList[j].firstedge = e;  /* 将当前顶点的指针指向 e */
                    
                    e = (EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间 */
                                        /* 生成边表结点 */
                    e->adjvex = i; /* 邻接序号为j */
                    e->next = G->adjList[j].firstedge; /* 将e指针指向当前顶点指向的结点 */
                    G->adjList[j].firstedge = e;  /* 将当前顶点的指针指向 e */
                }
            }
        3)十字链表
            十字链表的好处就是因为把邻接表和逆邻接表整合在了一起,这样既容易找到以vi为尾的弧,也容易找到以vi为头的弧,因
              而容易求得顶点的出度和入度。而且它除了结构复杂
    一点外,其实创建图算法的时间复杂度和邻接表相同,因此,再有向图中的
              应用中,十字链表是非常好的数据结构模型。
        4)邻接多重表
            邻接多重表与邻接表区别,仅仅是在于同一条表在邻接表中用两个结点表示,而在邻接多重表中只有一个结点,这样对边的操作
             就方便的多了,修改指向就可以了。
        5)边集数组
            边集数组是由两个一维数组构成,一个是存储顶点的信息;另一个是存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、
            终点下标(end)和权(weight)组成。
                ------------------------
                |begin  |  end | weight|
                ------------------------
                      其中begin是存储起点下标,end是存储终点下标,weight是存储权值
    3.图的遍历
        从图中某一顶点出发访遍图中其余顶点,且每一个顶点仅被访问一次,这个过程就要做图的遍历。
        对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通常由两种遍历次序方案:深度优先和广度优先遍历。
        1)深度优先遍历
            深度优先遍历(Depth_First_Search),也有称为深度优先搜索,简称DFS。
            深度优先遍历其实就是一个递归的过程。
            如果我们用的是邻接矩阵的方式,则代码如下:
            typedef int Boolean;  /* Boolean是布尔类型,其值是true或者false */
            Boolean  visitad[MAX];    /* 访问标志的数组 */
            /* 邻接矩阵的深度优先递归算法 */
            void DFS(MGraph G,int i)
            {
                int j;
                visitad[i] = TRUE;
                printf("%c", G.vexs[i]); /* 打印顶点,也可以其他操作 */
                for(j=0; j<G.numVertexes; j++)
                    if(G.arc[i][j] == 1 && !visited[j])
                            DFS(G,j);    /* 对为访问的邻接顶点递归调用 */
            }
            /* 邻接矩阵的深度优先遍历操作  */
            void DFSTraverse(MGraph G)
            {
                int i;
                for(i=0; i<G.numVertexes; i++)
                    visitad[i] = FALSE;   /* 初始化所有顶点状态都是未访问过的状态 */
                for(i = 0;i<G.numVertexes; i++)
                    if(!visitad[i]) /* 对未访问的顶点调用DFS,若是连通图,只会执行一次 */
                        DFS(G,i);
            }
            链表实现方法:
            /************ 邻接表的深度优先递归算法 *****************/
            void DFS(GraphAdjList GL, int i)
            {
                EdgeNode *p;
                visited[i] = TRUE;
                printf("%c",GL->adjList[i].data); /* 打印顶点,也可以其他操作 */
                p = GL->adjList[i].firstedge;
                while(p)
                {
                    if(!visited[p->adjvex])
                        DFS(GL,p->adjvex);/* 对未访问的邻接顶点递归调用 */
                    p= p->next;
                }
            }
            /**    邻接表的深度遍历操作           */
            void DFSTraverse(GraphAdjList GL)
            {
                int i;
                for(i =0;i< GL->numVertexes; i++)
                    visited[i] = FALSE; /** 初始化所欲偶顶点状态都是未访问过的状态 **/
                for(i =0;i<GL->numVertexes; i++)
                    if(!visited[i]) /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */
                        DFS(GL,i);
            }
        2)广度优先遍历
            广度优先遍历(Breadth_First_Search),又称为广度优先搜索,简称BFS.
            邻接矩阵结构的代码:
            /* 邻接矩阵的广度遍历算法 */
            void BFSTraverse(MGraph G)
            {
                int i,j;
                Queue Q;
                for(i=0;i<G.numVertexes;i++)
                    visited[i] = FALSE;
                InitQueue(&Q);   /* 初始化一辅助用的队列 */
                for(i=0;i<G.numVertexes;i++)/* 对每一个顶点做循环 */
                {
                    if(!visited[i])   /* 若是未访问过就处理 */
                    {
                        visited[i] = TRUE;/*设置当前顶点访问过*/
                        printf("%c",G.vexs[i]); /* 打印顶点,也可以其他操作*/
                        EnQueue(&Q,i);/*将此顶点入队列 */
                        while(!QueueEmpty(Q)) /*若当前队列不为空*/
                        {
                            DeQueue(&Q,&i); /* 将队中元素出队列,赋值给i */
                            for(j=0;j<G.numVertexes;j++)
                            {
                                /*判断其他顶点若与当前顶点存在边且未访问过 */
                                if(G.arc[i][j] == 1 && !visited[j])
                                {
                                    visited[j] = TRUE; /* 将找到的此顶点标记为已访问 */
                                    printf("%c ",G.vexs[j]); /* 打印顶点 */
                                    EnQueue(&Q,j);    /* 将找到的此顶点入队列 */
                                }
                            }
                        }
                    }
                }
            }
            邻接表的广度优先遍历,代码与邻接矩阵差异不大,代码如下:
            /* 邻接表的广度遍历算法 */
            void BFSTraverse(GraphAdjList GL)
            {
                int i;
                EdgeNode *p;
                Queue Q;
                for(i=0; i< GL->numVertexes; i++)
                {
                    visited[i] = FALSE;
                }
                InitQueue(&Q);
                for(i = 0;i<GL->numVertexes;i++)
                {
                    if(!visited[i])
                    {
                        visited[i] = TRUE;
                        printf("%c",GL->adjList[i].data); /*打印顶点,也可以其他操作*/
                        EnQueue(&Q,i);
                        while(!QueueEmpty(Q))
                        {
                            DeQueue(&Q,&i);
                            p = GL->adjList[i].firstedge;/*找到当前顶点边表链表头指针*/
                            while(p)
                            {
                                if(!visited[p->adjvex]) /*若此顶点未被访问*/
                                {
                                    visited[p->adjvex] = TRUE;
                                    printf("%c ",GL->adjList[p->adjvex].data);
                                    EnQueue(&Q,p->adjvex); /* 将此顶点入队列*/
                                }
                                p = p->next; /* 指针指向下一个邻接点*/
                            }
                        }
                    }
                }
            }
    4.最小生成树
        构造连通网的最小代价生成树称为最小生成树。
        找连通图的最小生成树,经典算法有两种,普里姆算法和克鲁斯卡尔算法。
        1)普利姆(Prim)算法
            /* Prim 算法最小生成树 */
            void MiniSpanTree_Prim(MGraph G)
            {
                int min,i,j,k;
                int adjvex[MAXVEX];/* 保存相关顶点下标 */
                int lowcost[MAXVEX]; /* 保存相关顶点间边的权值 */
                lowcost[0] = 0; /*初始化第一个权值为0 ,即v0加入生成树 */
                                /*lowcost 的值为0,在这里就是此下标的顶点已经加入生成树*/
                adjvex[0] = 0; /*初始化第一个顶点下标为0*/
                for(i=1;i<G.numVertexes; i++)  /*循环除下标为0外的全部结点*/
                {
                    lowcost[i] = G.arc[0][i]; /*将v0顶点与之有边的权值存入数组*/
                    adjvex[i] = 0; /* 初始化都为v0的下标*/
                    
                }
                for(i = 0;i<G.numVertexes; i++)
                {
                    if(lowcost[j] != 0 && lowcost[j] < min)
                    {/*如果权值不为0,且权值小于min */
                        min = lowcost[j]; /* 则让当前权值成为最小值 */
                        k = j;/*将当前权值成为最小值*/
                    }
                    j++;
                }
                printf("(%d,%d)", adjvex[k],k); /*打印当前顶点边中权值最小边 */
                lowcost[k] = 0; /* 将当前权值设置为 0 ,表示此顶点已经完成任务 */
                for(j = 1;j< G.numVertexes; j++)
                {
                    if(lowcost[j] != 0 && G.arc[k][j] < lowcost[j])
                    {
                        /* 若下标为k顶点各边权值小于此前这些顶点未被加入生成树权值*/
                        lowcost[j] = G.arc[k][j]; /*将较小的权值存入lowcost */
                        adjvex[j] = k;   /* 将下标为k的顶点存入adjvex*/
                    }
                }
            }
        2)克鲁斯卡尔(Kruskal)算法
            普利姆(Prim)算法是以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树的。我们可以直接就以边为目标去构建,因为权值是在边上,直接去找
        最小权值的边来构建生成最小树。
        edge边集数组结构的定义代码:
        /*对边集数组Edge结构的定义*/
        typedef struct
        {
            int begin;
            int end;
            int weight;
        }Edge;
        克鲁斯卡尔算法代码如下,左侧数字为行号。其中MAXEDGE为边数量的极大值,此处大于等于15即可,MAXVEX为顶点个数最大值,此处大于等于9即可。
        实现代码:
        /* Kruskal 算法生成最小生成树 */
        void MiniSpanTree_Kruskal(MGraph G)  /* 生成最小生成树 */
        {
            int i,n,m;
            Edge edges[MAXEDGE]; /*定义边集数组 */
            int parent[MAXVEX]; /*定义一数组用来判断边与边是否形成环路 */
            /* 此处省略将邻接矩阵G转化为边集数组edges并按由小到大排序的代码*/
            for(i = 0; i< G.numVertexes; i++)
                parent[i] = 0;  /*初始化数组值为0 */
            for(i=0;i<G.numEdges; i++) /*循环每一条边 */
            {
                n = Find(parent,edges[i].begin);
                m = Find(parent,edges[i].begin);
                if(n != m) /*假如n与m不等,说明此边没有与现有生成树形成环路 */
                {
                    parent[n] = m;  /* 将此边的结尾顶点放入下标为顶点的parent中*/
                                    /* 表示此顶点已经在生成树集合中*/
                    printf(" (%d, %d) %d ",edges[i].begin,edges[i].end,edges[i].weight);
                }
            }
        }
        
        int Find(int *parent, int f)/* 查找连线顶点的尾部下标 */
        {
            while(parent[f] > 0)
                f = parent[f];
            return  f;
        }
    5.最短路径
            1)迪杰斯特拉(Dijkstra)算法
                按路径长度递增的次序产生最短路径的算法。
                代码实现:
                #define MAXVEX 9
                #define INFINITY 65535
                typedef int Pathmatirx[MAXVEX];  /*用于存储最短路径下标的数组*/
                typedef int ShortPathTable[MAXVEX];  /*用于存储到各点最短路径的权值和*/
                /* Dijkstra算法,求有向网G的v0顶点到其余顶点v最短路径P[v]及带权长度D[v]*/
                /* P[v]的值为前驱顶点下标,D[v]表示v0到v的最短路径长度和 */
                void ShortestPath_Dijkstra(MGraph G, int v0, Pathmatirx *P,ShortPathTable *D)
                {
                    int v,w,k,min;
                    int final[MAXVEX]; /* final[w] = 1 表示求得顶点v0至vw的最短路径 */
                    for(v =0;v<G.numVertexes; v++)
                    {
                        final[v] = 0; /*全部顶点初始化为未知最短路径状态 */
                        (*D)[v] = G.matirx[v0][v]; /* 将与v0点右连线的点加上权值 */
                        (*p)[v] = 0; /*初始化路径数组p为 0 */
                    }
                    (*D)[v0] = 0; /*v0至v0的路径为0 */
                    final[v0] = 1; /* v0 至v0不需要路径 */
                    /* 开始主循环,每次求得v0到某个v顶点的最短路径 */
                    for(v = 1; v < G.numVertexes; v++)
                    {
                        min = INFINITY;  /*当前所知离v0顶点的最近距离*/
                        for(w = 0; w<G.numVertexes; w++)  /*寻找离v0最近的顶点 */
                        {
                            if(!final[w] && (*D)[w]<min)
                            {
                                k = w;
                                min = (*D)[w]; /*w顶点离v0顶点最近*/
                            }
                        }
                        final[k] = 1; /* 将目前找到的最近的顶点置为1 */
                        for(w= 0; w<G.numVertexes; w++)  /*修正当前最短路径及距离*/
                        {
                            /* 如果经过v顶点的路径比现在这条路径的长度短的话*/
                            if(!final[w] && (min + G.matirx[k][w] < (*D)[w]))
                            {
                                /*说明了找到了更短的路径,修改D[w]和P[w] */
                                (*D)[w] = min + G.matirx[k][w]; /* 修改当前路径长度 */
                                (*p)[w] = k;
                            }
                        }
                    }
                }
            2)弗洛伊德(Floyd)算法
                这个算法涉及到了矩阵的知识,上代码
                typedef int Pathmatirx[MAXVEX][MAXVEX];
                typedef int ShortPathTable[MAXVEX][MAXVEX];
                /*Floyd算法,求网图G中各顶点v到其余顶点w最短路径P[v][w]及带权长度D[v][w] */
                void ShortestPath_Floyd(MGraph G, Pathmatirx *p, ShortPathTable *D)
                {
                    int v,w,k;
                    for(v=0;v<G.numVertexes; ++v)/*初始化D与P*/
                    {
                        for(w=0;w<G.numVertexes; ++w)
                        {
                            (*D)[v][w] = G.matirx[v][w]; /* D[v][w]值即为对应点间的权值 */
                            (*P) [v][w] = w; /*初始化P */
                        }
                    }
                    for(k = 0; k<G.numVertexes; ++k)
                    {
                        for(v = 0;v < G.numVertexes; ++v)
                        {
                            for(w = 0;w<G.numVertexes; ++w)
                            {
                                if((*D)[v][w] > (*D)[v][k] + (*D)[k][w])
                                {
                                    /* 如果净多下标为k顶点路径比原两点间路径更粗*/
                                    /* 将当前两点间权值设为更小的一个 */
                                    (*D)[v][w] = (*D)[v][w] + (*D)[k][w];
                                    (*P)[v][w] = (*P)[v][k]; /*路径设置净多下标为k的顶点*/
                                }
                            }
                        }
                    }
                }
    6.拓扑排序
        在一个表示工程的有向图中,用顶点表示活动,用弧表示活动之间的优先关系,这样的有向图为顶点表示活动的网,我们称为AOV网。
        所谓拓扑排序,其实就是对一个有向图构造拓扑序的过程。
        1)拓扑排序算法
            基本思路:
                从AOV网中选择一个入度为0的顶点输出,然后删去此顶点,并删除以此为顶点为尾的弧,继续重复此步骤,直到输出全部顶点
            或者AOV网中不存在入度为0的顶点为止。
        拓扑排序中,设计到的结构代码如下:
        typedef struct EdgeNode    /*边表结点*/
        {
            int adjvex; /* 邻接点域,存储该顶点对应的下标 */
            int weight;   /*用于存储权值,对于非同图可以不需要 */
            struct RdgeNode *next;   /* 链域,指向下一个邻接点 */
        }EdgeNode;
        
        
        typedef struct VertexNode   /* 顶点表结点 */
        {
            int in;        /* 顶点入度 */
            int data;      /* 顶点域,存储顶点信息 */
            EdgeNode *firstedge; /* 边表头指针 */
        }VertexNode,AdjList[MAXVEX];
        
        typedef struct
        {
            AdjList adjList;
            int numVertexes,numEdges; /* 图中当前顶点数和边数 */
        }graphAdjList, *GraphAdjList;
        /* 拓扑排序,若GL无回路,则输出拓扑排序序列并返回OK,若有回路则返回ERROR */
        Status TopologicalSort(GraphAdjList GL)
        {
            EdgeNode *e;
            int i,k,gettop;
            int top = 0;  /*用于栈指针下标 */
            int count = 0; /* 用于统计输出顶点的个数 */
            int *stack;  /* 建栈存储入度为0 的顶点 */
            stack = (int *)malloc(GL->numVertexes *sizeof(int));
            for(i = 0;i<GL->numVertexes; i++)
                if(GL->adjList[i].in == 0)
                        stack[++top] = i; /* 将入度为0 */
            while(top != 0)
            {
                gettop = stack[top--]; /* 出栈 */
                printf("%d -> ",GL->adjList[gettop].data); /* 打印此顶点 */
                count++; /*统计输出顶点数 */
                for(e = GL->adjList[gettop].firstedge; e; e=e->next)
                {/* 对此顶点弧表遍历 */
                    k = e->adjvex;
                    if(!(--GL->adjList[k].in))/*将k号顶点邻接点的入度减为1*/
                        stack[++top] = k; /* 若为0则入栈,以便于下次循环输出 */
                }
            }
            
            if(count < GL->numVertexes) /* 如果count 小于顶点数 ,说明存在环 */
            {
                return ERROR;
            }
            else
                return OK;
                        
        }
    7.关键路径
        拓扑排序主要是为解决一个工程能否顺序进行的问题,但有时我们还需要解决工程完成需要的最短时间问题。
        在AOV网的基础上,我们来介绍一个新的概念。在一个表示工程的带权有向图中,用顶点表示事件,用有向边表示活动,用边上的权值表示活动的持续时间,
        这种有向图的边表示活动的网,我们称之为AOE网。我们把AOE网中没有入边的顶点称为始点或源点,没有出边的顶点称为终点或汇点。
        需要几个参数:
            前两个事件是预计:
            1.事件的最早发生时间etv(earlist time of vertex):即顶点vk的最早发生时间。
            2.事件的最晚发生时间ltv(latest time of vertex):即顶点vk的最晚发生时间,也就是每个顶点对应的事件最晚需要开始的时间,超过此时间将会延误整个工期。
            这两个事件开始做的时间预计:
            3.活动的最早开工时间ete (earlist time of edge):即弧ak的最晚发生时间,也就是不推迟工期的最晚开工时间。
            4.活动的最晚开工时间lte(latest time of edge):即弧ak的最晚发生时间,也就是不推迟工期的最晚开工时间。
        关键路径算法:
            将AOE网转化为邻接表结构,注意与拓扑排序时邻接表结构不同的地方在于,这里弧链表增加了weight域,用来存储弧的权值。
            首先声明几个全局变量:
            int *etv,*ltv;  /* 事件最早发生时间和最迟发生时间数组 */
            int *stack2;   /* 用于存储拓扑序列的栈 */
            int top2;     /* 用于stack2的指针 */
            
            /* 拓扑排序,用于关键路径计算 */
            Status TopologicalSort(GraphAdjList  GL)
            {
                EdgeNode  *e;
                int i,k,gettop;
                int top = 0; /* 用于栈指针下标 */
                int count;  /* 用于统计输出顶点的个数 */
                int *stack;  /* 建栈将入度为0的顶点入栈 */
                stack = (int *)malloc(GL->numVertexes *sizeof(int));
                for(i=0; i<GL->numVertexes; i++)
                    if(0 == GL->adjList[i].in)
                        stack[++top] = i;
                top2 = 0;   /*初始化为0 */
                etv = (int*)malloc(GL->numVertexes * sizeof(int)); /* 时间最早发生时间 */
                for(i=0; i<GL->numVertexes; i++)
                    etv[i] = 0;  /* 初始化为0 */
                stack2 = (int*)malloc(GL->numVertexes * sizeof(int));  /* 初始化 */
                while(top != 0)
                {
                    gettop = stack[top--];
                    count++;
                    stack2[++top2] = gettop; /* 将弹出的顶点序号压入拓扑序列的栈 */
                    
                    for(e = GL->adjList[gettop].firstedge;e;e = e->next)
                    {
                        k = e->adjvex;
                        if(!(--GL->adjList[k].in))
                            stack[++top] = k;
                        if((etv[gettop]+ e->weight)>etv[k]) /* 求各顶点事件最早发生时间值 */
                            etv[k] = etv[gettop] + e->weight;
                    }
                }
                if(count < GL->numVertexes)
                    return ERROR;
                else
                    return OK;
            }
        
       

    The future's not set,there is no fate but what we make for ourselves.
  • 相关阅读:
    使用webstorm来创建并且运行vue项目详细教程
    Jmeter后置处理器之Json提取器
    Linux常用命令
    Linux安装Mysql
    Linux安装JDK
    redhat6.5安装yum
    Linux安装python
    Linux安装Tomcat
    搞懂Redis协议RESP
    1TB是多大?
  • 原文地址:https://www.cnblogs.com/wang1994/p/9115483.html
Copyright © 2011-2022 走看看