作者: unravelW | 来源:发表于2018-07-01 23:07 被阅读0次

    图的定义与术语

    1、图按照有无方向分为无向图和有向图。无向图由顶点和边构成,有向图由顶点和弧构成。弧有弧尾和弧头之分。

    2、如果任意两个顶点之间都存在边叫完全图,有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。

    3、图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做度,有向图顶点分为入度和出度。

    4、图上的边或弧上带权则称为网。

    5、图中顶点间存在路径,两顶点存在路径则说明是连通的,如果路径最终回到起始点则称为环,当中不重复叫简单路径。若任意两顶点都是连通的,则图就是连通图,有向则称为强连通图。

    6、无向图中连通且n个顶点n-1条边叫生成树。有向图中一顶点入度为0其余顶点入度为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("输入顶点数和边数:\n");

        scanf("%d,%d",&G->numVertexes,&G->numEdges);/*输入顶点数和边数*/

        for(i=0;i<G->numVertexes;i++)/*读入顶点信息,建立顶点表*/

              scanf(&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:\n");

                 scanf("%d,%d,%d",&i,&j,&w);/*输入边(vi,vj)上的权w*/

                G->arc[i][j]=w;

                G->arc[j][i] =G->arc[i][j];/*因为是无向图,矩阵对称*/

           }

    }

    邻接表

    邻接表存储方法:

    1、图中顶点用一维数组存储,每个数据元素存储指向第一个邻接点的指针,以便于查找该顶点的边信息。

    2、图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。

    结点定义代码:

    typedef char  VertexType;/*顶点类型应由用户定义*/

    typedef  int EdgeType;/*边上的权值类型应由用户定义*/

    typedef  struct  EdgeNode/*边表结点*/

    {
        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("输入顶点数和边数:\n");

          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)上的顶点序号:\n");

                     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[i].firstedge=e;/*将当前顶点的指针指向e*/

                     e=(EdgeNode*)malloc(sizeof(EdgeNode));/*向内存申请空间,生成边表结点*/

                      e->adjvex=i;

                     e->next=G->adjList[j].firstedge;/*将e指针指向当前顶点指向的结点*/

                     G->adjList[j].firstedge=e;/*将当前顶点的指针指向e*/

               }

    }

    剩余的图的存储结构还有,十字链表,邻接多重表,边集数组。

    图的遍历

    深度优先遍历

    //邻接矩阵的深度优先遍历

    typedef int Boolean;  /*Boolean是布尔类型,其值是TRUE或FALSE*/

    Boolean visited[MAX];/*访问标志的数组*/

    /*邻接矩阵的深度优先递归算法*/

    void DFS(MGraph G, int i) {

        int j;

        visited[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++)

                      visited[i] = FALSE;/*初始所有顶点状态都是未访问状态*/

          for(i = 0; i < G.numVertexes; i++)

                      if(!visited[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);

    }

    广度优先遍历

    /*邻接矩阵的广度优先遍历*/

    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 *po;

             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(!visitied[p->adjvex]) {

                                                                         visited[p->adjvex] = TRUE;

                                                                           printf("%c", GL->adjList[p->adjvex].data);

                                                                           EnQueue(&Q, p ->adjvex);/*将此顶点入队列*/

                                                                 }

                                                                 p=p->next;

                                                          }

                                                 }

                                     }

                  }

    }

    最小生成树

    /*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++) {

                           min = INFINITY;/*初始化最小权值*/

                          j = 1; k = 0;

                          while(j < G.numVertexes) { /*循环全部顶点*/

                                    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] != && G.arc[k][j] <lowcost[j]) {

                                               /*若小标为k顶点各边权值小于此前这些顶点未被加入生成树权值*/

                                                                  lowcost[j] = G.arc[i][j];/*将较小权值存入lowcost*/

                                                                   adjvex[j] = k;/*将下标为k的顶点存入adjvex*/

                                                   }

                                       }

                        }

    }

    /*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].end);

                        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;

    }

    最短路径

    对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。

    Dijkstra算法

    #define  MAXVEX  9

    #define INFINITY 65535

    typedef  int  Patharc[MAXVEX];/*用于存储最短路径下标的数组*/

    typdef  int ShortPathTable[MAXVEX];/*用于存储到各点最短路径的权值和*/

    /*Dijkstra算法,求有向网G的V0顶点到其余顶点V最短路径P[v]及带权长度D[v]*/

    /* P[v]的值为前驱顶点下标,D[v]表示V0到V的最短路径长度和。*/

    void  ShortestPath_Dijkstra (MGraph G,int v0, Patharc *p, ShortPathTable *D) {

        int v, w, k ,min;

         int  final[MAXVEX];/*final[w]=1表示求得顶点V0至Vw的最短路径*/

         for(v=0; v< G.numberVertexes; v++)/*初始化数据*/

         {

                   final[v]= 0;/*全部顶点初始化为未知最短路径状态*/

                    (*D)[v]= G.arc[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.arc[k][w] < (*D)[w]))

                                             {/*说明找到了更短的路径,修改D[w]和P[w]*/

                                                            (*D)[w] = min+G.arc[k][w];/*修改当前路径长度*/

                                                              (*P)[w]= k;

                                                 }

                                    }

                   }

    }

    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][k] + (*D)[k][w];

                                                                      (*P)[v][w]= (*P)[v][k];/*路径设置经过下标为k的顶点*/        

                                              }

                                 }

                   }

         }

    }

    求最短路径代码

    for(v=0; v< G.numVertexes; v++)

    {

              for(w = v+ 1; w < G.numVertexes; w++)

              {

                          printf("v%d-v%d weight:%d",v, w, D[v][w]);

                         k =P[v][w];/*获得第一个路径顶点下标*/

                          printf("path: %d",v);/*打印源点*/

                           while(k!=w)/*如果路径顶点下标不是终点*/

                           {

                                      printf("-> %d", k );/*打印路径顶点*/

                                        k=P[k][w];/*获得下一个路径顶点下标*/

                             }

                            printf("-> %d\n",w);/*打印终点*/

                    }

                     printf("\n");

    }

    拓扑排序

    设G=(V,E)是一个具有n个顶点的有向图,V中的顶点序列v1,v2,·····,vn,满足若从顶点vi到vj有一条路径,则在顶点序列中顶点vi必在顶点vj之前。则我们称这样的顶点序列为一个拓扑序列。

    所谓拓扑排序,其实就是对一个有向图构造拓扑序列的过程。

    拓扑排序算法

    结构代码:

    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;

    /*拓扑排序,若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;

    }

    关键路径

    路径上各个活动所持续的时间之和称为路径长度,从源点到汇点具有最大长度的路径叫关键路径,在关键路径上的活动叫关键活动。

    关键路径算法

    全局变量:

    int *etv,*ltv;/*事件最早发生时间和最迟发生时间数组*/

    int *stack2;/*用于存储拓扑序列的栈*/

    int top2;/*用于stack2的指针*/

    /*拓扑排序,用于关键路径计算*/

    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(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;

    }

    /*求关键路径,GL为有向网,输出GL的各项关键活动*/

    void CriticalPath(GraphAdjList GL) {

             EdgeNode *e;

             int  i, gettop, k ,j;

              int ete, lte;/*声明活动最早发生时间和最迟发生时间变量*/

              TopologicalSort(GL);/*求拓扑序列,计算数组etv和stack2的值*/

                ltv = (int *)malloc(GL->numVertexes * sizeof(int));/*事件最晚发生时间*/

              for(i = 0; i < GL->numVertexes; i++)

                       ltv[i] = etv[GL->numVertexes - 1];/*初始化ltv*/

               while(top2 ! = 0)/*计算ltv*/

              {

                         gettop=stack2[top2--];/*将拓扑序列出栈,后进先出*/

                        for(e = GL->adjList[gettop].firstedge; e; e = e->next) {

                         /*求各顶点事件的最迟发生时间ltv值*/

                                    k = e->adjvex;

                                    if(ltv[k]- e->weight < ltv[gettop])/*求各顶点事件最晚发生时间ltv*/

                                              ltv[gettop] = ltv[k] - e->weight;

                             }

                 }

                 for(j=0; j < GL->numVertexes; j++)/*求ete,lte和关键活动*/

                 {

                            for(e->GL->adjList[j].firstedge; e; e= e->next) {

                             {

                                           k= e->adjvex;

                                            ete =etv[j];/*活动最早发生时间*/

                                            lte=ltv[k] -e->weight;/*活动最迟发生时间*/

                                            if(ete == lte)/*两者相等即在关键路径上*/

                                                      printf("<v%d,v%d> length: %d,",GL->adjList[j].data, GL->adjList[k].data,e->weight);

                                     }

                      }

    }

    相关文章

      网友评论

          本文标题:

          本文链接:https://www.haomeiwen.com/subject/aimjjftx.html