zoukankan      html  css  js  c++  java
  • 算法导论 第22章 图论之拓扑排序

    邻接表

        邻接矩阵用二维数组即可存取,比较简单,但除完全图外,一般的图不是任意两个顶点都相邻接,因此邻接矩阵也有很多零元素,特别是当n 较大而边数相对完全图的边(n-1)又少得多时,邻接矩阵仍是很稀疏,这样浪费存储空间。

        邻接表(Adjacency List)是图的一种顺序存储与链式存储结合的存储方法,类似于树的孩子链表表示法。由于它只考虑非零元素,因而节省了零元素所占的存储空间。它对于无向图和有向图都适用。

        邻接表示法就是对于图G中的每个顶点放到一个数组中,数组的每个元素存放一个结点并指向一个单链表的指针。链表中存储着与该顶点相邻接的顶点所在的数组元素的下标。在邻接表表示中有两种结点结构,如图6-9所示。

    1

    (a) 表头结点             (b) 边表结点

    图6-9  邻接矩阵表示的结点结构

        在邻接表中,对图中每个顶点建立一个单链表。单链表有一个表头结点,表头结点的结构为图6-9(a)所示。其中,vertex域存放图中某个顶点vi 的信息,link为指针,指向对应单链表中的结点。

        单链表中的结点称为边表结点,边表结点结构如图6-9(b)所示。其中,adjvex域存放与顶点vi相邻接的顶点在二维数组中的序号,next域为指针,指向与顶点vi相邻接的下一个顶点的边表结点。

    下图6-10给出无向图6-7对应的邻接表表示。

    2

    图6-10  图的邻接表表示

    拓扑排序

    拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。

    通常我们把计划、施工过程、生产流程、程序流程等都当成一个工程,一个大的工程常常被划分成许多较小的子工程,这些子工程称为活动。这些活动完成时,整个工程也就完成了。

    我们用一种有向图来表示这些工程、计划等,在这种有向图中,顶点表示活动,有向边表示活动的优先关系这种用顶点表示活动,用弧来表示活动间的优先关系的有向图叫做顶点表示活动的网络(Actire On Vertices)简称为AOV网。

    拓扑排序:

      假设G=(VE)是一个具有n个顶点的有向图,V中顶点序列vlv2,…,vn称做一个拓扑序列(Topological Order),当且仅当该顶点序列满足下列条件:若在有向图G中存在从顶点vivj的一条路径,则在顶点序列中顶点vi必须排在顶点vj之前。通常,在AOV网中,将所有活动排列成一个拓扑序列的过程叫做拓扑排序(Topological Sort)

    AOV网中不应该出现有向环。因为环的存在意味着某项活动将以自己为先决条件,显然无法形成拓扑序列。

       判定网中是否存在环的方法:对有向图构造其顶点的拓扑有序序列,若网中所有顶点都出现在它的拓扑有序序列中,则该AOV网中一定不存在环。

    4、 拓扑排序的算法思想

        输入AOV网络。令 n 为顶点个数。      

    1)在AOV网络中选一个没有直接前驱的顶点,并输出之;

    2)从图中删去该顶点同时删去所有它发出的有向边;

        重复以上步骤,直到全部顶点均已输出,拓扑有序序列形成,拓扑排序完成;或图中还有未输出的顶点,但已跳出处理循环。这说明图中还剩下一些顶点,它们都有直接前驱,再也找不到没有前驱的顶点了。这时AOV网络中必定存在有向环。

     
     

    c语言实现

    头文件graph_adj.h:

     1 #ifndef _GRAPH_ADJ_H
     2 #define _GRAPH_ADJ_H
     3 #define MAX_STR 20
     4 typedef char Vertextype[MAX_STR];
     5 #define maxvernum 100       //最大顶点数为100
     6 typedef struct node         //边表结点
     7 { 
     8     int adjvex;             //邻接点域
     9     struct node  * next;    //指向下一个邻接点的指针域
    10 }Edgenode;   
    11                   
    12 typedef struct vnode        //表头结点
    13 { 
    14     Vertextype vertex;      //顶点域
    15     Edgenode  *link;        //边表头指针
    16  } Vexnode;
    17  
    18 typedef Vexnode Adjlist[maxvernum];  //adjlist是邻接表类型
    19 
    20 typedef struct
    21 {  
    22     Adjlist adjlist;                     //邻接表
    23     int n,e;                             //顶点数和边数
    24 } Adjgraph;                 // Adjgraph是以邻接表方式存储的图类型
    25 #endif

    实现文件topsort.c:

      1 /*拓扑排序*/
      2 
      3 #include "graph_adj.h"
      4 #include "topsort.h"
      5 #include "queue.h"
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 /*scanf 
      9 函数原型:
     10   int scanf( const char *format, ... );
     11   scanf()函数是格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。
     12   其调用格式为: scanf("<格式化字符串>",<地址表>);
     13     函数 scanf() 是从标准输入流 stdin 中读内容的通用子程序,可以读入全部固有类型的数据并自动转换成机内形式。
     14 返回值:
     15     函数返回成功赋值的数据项数,读到文件末尾出错时则返回EOF。*/
     16 void init_graph(Adjgraph *g) 
     17 {
     18     int i,j,k;
     19     Edgenode * s;
     20     printf("请输入顶点数和边数(输入格式为:顶点数,边数):\n");
     21     scanf("%d,%d",&(g->n),&(g->e));     //读入顶点数和边数
     22     printf("请输入顶点信息(输入格式为:顶点号<CR>):\n");
     23     for (i=0;i<g->n;i++)                //建立有n个顶点的顶点表
     24     { 
     25         scanf("%s",&(g->adjlist[i].vertex));  //读入顶点信息   g->adjlist[i].link=NULL;     //顶点的边表头指针设为空
     26         g->adjlist[i].link = NULL;//链接初始化
     27         printf("%s\t", &(g->adjlist[i].vertex));
     28     }
     29     printf("\n");
     30     printf("请输入边的信息(输入格式为:i,j):\n");
     31     for (k=0;k<g->e;k++)                //建立边表
     32     { 
     33         scanf("%d,%d",&i,&j);         //读入边<vi,vj>的顶点对应序号
     34         printf("%d -> %d\t", i, j);
     35         s=(Edgenode*)malloc(sizeof(Edgenode));  //生成新边表结点s
     36         s->adjvex=j;                      //邻接点序号为j
     37         //将新边表结点s插入到顶点vi的边表头部
     38         s->next=g->adjlist[i].link;      
     39         g->adjlist[i].link=s;
     40     }  
     41     printf("\n");
     42 }
     43 //拓扑排序
     44 int* topsort(Adjgraph *g)
     45 {
     46     Queue Q;
     47     Edgenode *e;
     48     Vexnode V,W;
     49     int *Indegree, *TopNum, i, counter = 0, v;
     50     Indegree = (int *)malloc(sizeof(int) * g->n);
     51     TopNum = (int *)malloc(sizeof(int) * g->n);
     52     if(Indegree == NULL)
     53     {
     54         printf("Failed to malloc Indegree\n");
     55         return;
     56     }
     57     for(i = 0; i < g->n; i ++)        //对每个顶点的入度数初始化为零
     58         Indegree[i] = 0;
     59     for(i = 0; i < g->n; i ++)        //统计每个顶点的入度数
     60     {    
     61         e = g->adjlist[i].link;
     62         while(e != NULL)
     63         {
     64             Indegree[e->adjvex] ++;
     65             e = e->next;
     66         }
     67     }
     68     printf("Indegree:\n");            //打印每个顶点的入度数
     69     for(i = 0; i < g->n; i ++)
     70         printf("%s:[%d]\t", g->adjlist[i].vertex, Indegree[i]);
     71     printf("\n");
     72     Q = createqueue(g->n);            //开辟队列
     73     makeempty(Q);                    //队列初始化
     74     for(i = 0; i < g->n; i ++)        //将所有入度为0的结点压入队列
     75         if(Indegree[i] == 0)
     76         {
     77             printf("%s enqueue\n", g->adjlist[i].vertex);
     78             enqueue(i, Q);
     79         }
     80     while(!isempty(Q))
     81     {
     82         v = frontanddequeue(Q);
     83         printf("%s dequeue\n", g->adjlist[v].vertex);
     84         TopNum[v] = ++counter;
     85         e = g->adjlist[v].link;
     86         while(e != NULL)
     87         {
     88             Indegree[e->adjvex] = Indegree[e->adjvex] - 1;
     89             if( Indegree[e->adjvex] == 0)
     90             {
     91                 //打印每个顶点的入度数//////////////
     92                 printf("Indegree:\n");
     93                 for(i = 0; i < g->n; i ++)
     94                         printf("%s:[%d]\t", g->adjlist[i].vertex, Indegree[i]);
     95                 printf("\n");
     96                 printf("%s enqueue\n",  g->adjlist[e->adjvex].vertex);
     97                 ////////////////////////////////////
     98                 enqueue(e->adjvex, Q);
     99                 
    100             }
    101             e = e->next;    
    102         }
    103     }
    104     if(counter != g->n)
    105     {
    106         printf("Graph has a cycle\n");
    107         return NULL;
    108     }
    109     disposequeue(Q);
    110     return TopNum;
    111 }

    队列头文件queue.h:

    View Code
     1 #ifndef _Queue_h
     2 #define _Queue_h
     3 typedef int ElementType;
     4 struct QueueRecord;
     5 typedef struct QueueRecord *Queue;
     6 int isfull(Queue Q);
     7 int isempty(Queue Q);
     8 Queue createqueue(int MaxElement);
     9 void disposequeue(Queue Q);
    10 void makeempty(Queue Q); 
    11 void enqueue(ElementType X, Queue Q);
    12 ElementType front(Queue Q);
    13 void dequeue(Queue Q);
    14 ElementType frontanddequeue(Queue Q);
    15 #endif

    队列实现头文件queue.c

    View Code
      1 #include "queue.h"
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 struct QueueRecord
      5 {
      6         int capacity;
      7         int front;
      8         int rear;
      9         int size;
     10         ElementType *array;
     11 };
     12 
     13 int isempty(Queue Q)
     14 {
     15     return Q->size == 0;
     16 }
     17 
     18 int    isfull(Queue Q)
     19 {
     20     return Q->size == Q->capacity;
     21 }
     22 
     23 Queue createqueue(int MaxElement)
     24 {
     25     ElementType *array;
     26     Queue queue;
     27     queue = (Queue)malloc(sizeof(struct QueueRecord));
     28     if(queue == NULL)
     29     {
     30         printf("Failed to malloc queue\n");
     31         return NULL;
     32     }
     33     array = (ElementType *) malloc(sizeof(ElementType) * MaxElement);
     34     if(array == NULL)
     35     {
     36         printf("Failed to malloc queue\n");
     37         free(queue);
     38         return NULL;
     39     }
     40     queue->array = array;
     41     queue->capacity = MaxElement;
     42     return queue;
     43 }
     44 
     45 
     46 void disposequeue(Queue Q)
     47 {
     48     if(Q != NULL && Q->array != NULL)
     49     {
     50         free(Q->array);
     51         Q->array = NULL;
     52     }
     53     else if( Q != NULL)
     54     {
     55         free(Q);
     56         Q = NULL;
     57     }
     58 }
     59 void makeempty(Queue Q)
     60 {
     61     Q->size = 0;
     62     Q->front = 1;
     63     Q->rear = 0;
     64 }
     65 
     66 void enqueue(ElementType X, Queue Q)
     67 {
     68     if(isfull(Q))
     69     {
     70         printf("Full Queue\n");
     71         return;
     72     }
     73     else
     74     {
     75         Q->size ++;
     76         if(Q->rear != Q->capacity - 1)
     77             Q->rear ++;
     78         else 
     79             Q->rear = 0;
     80         /*printf("%d -> Q[%d]\n", X, Q->rear);*/
     81         Q->array[Q->rear] = X;
     82     }
     83 }
     84 
     85 ElementType front(Queue Q)
     86 {
     87     if(isempty(Q))
     88         return -1;
     89     else
     90         return Q->array[Q->front];
     91 }
     92 
     93 void dequeue(Queue Q)
     94 {
     95     if(isempty(Q))
     96     {
     97         printf("Empty Queue\n");
     98         return;
     99     }
    100     else
    101     {
    102         Q->size --;
    103         /*printf("out <- Q[%d]\n", Q->front);*/
    104         if(Q->front != Q->capacity - 1)
    105             Q->front ++;
    106         else
    107             Q->front = 0;
    108         
    109     }
    110 }
    111 /**/
    112 ElementType frontanddequeue(Queue Q)
    113 {
    114     ElementType value;
    115     if(isempty(Q))
    116     {
    117         printf("Empty Queue\n");
    118         return -1;
    119     }
    120     else
    121     {
    122         value = front(Q);
    123         dequeue(Q);
    124         return value;
    125     }
    126 }

    主函数topsort_main.c

     1 #include "graph_adj.h"
     2 #include "topsort.h"
     3 #include <stdio.h>
     4 #include <stdlib.h>
     5 
     6 int main(void)
     7 {
     8     Adjgraph *g;
     9     int i;
    10     int *topnum;
    11     g = (Adjgraph *)malloc(sizeof(Adjgraph));
    12     init_graph(g);
    13     topnum = topsort(g);
    14     printf("拓扑排序:(格式为:结点名称:结点id:排序序号)\n");
    15     if(topnum != NULL)
    16     {
    17         for(i = 0;i < g->n; i++)
    18             printf("%s:%d:%d\n", g->adjlist[i].vertex, i, topnum[i]);
    19     }
    20     free(topnum);
    21 }

    按照上图拓扑顺序输入数据:

    View Code
     1 7,12
     2 v1
     3 v2
     4 v3
     5 v4
     6 v5
     7 v6
     8 v7
     9 0,3
    10 0,1
    11 0,2
    12 1,3
    13 1,4
    14 2,5
    15 3,2
    16 3,5
    17 3,6
    18 4,3
    19 4,6
    20 6,5

    运行结果如下:

    请输入顶点数和边数(输入格式为:顶点数,边数):
    请输入顶点信息(输入格式为:顶点号<CR>):
    v1    v2    v3    v4    v5    v6    v7    
    请输入边的信息(输入格式为:i,j):
    0 -> 3    0 -> 1    0 -> 2    1 -> 3    1 -> 4    2 -> 5    3 -> 2    3 -> 5    3 -> 6    4 -> 3    4 -> 6    6 -> 5    
    Indegree:
    v1:[0]    v2:[1]    v3:[2]    v4:[3]    v5:[1]    v6:[3]    v7:[2]    
    v1 enqueue
    v1 dequeue
    Indegree:
    v1:[0]    v2:[0]    v3:[1]    v4:[3]    v5:[1]    v6:[3]    v7:[2]    
    v2 enqueue
    v2 dequeue
    Indegree:
    v1:[0]    v2:[0]    v3:[1]    v4:[2]    v5:[0]    v6:[3]    v7:[2]    
    v5 enqueue
    v5 dequeue
    Indegree:
    v1:[0]    v2:[0]    v3:[1]    v4:[0]    v5:[0]    v6:[3]    v7:[1]    
    v4 enqueue
    v4 dequeue
    Indegree:
    v1:[0]    v2:[0]    v3:[1]    v4:[0]    v5:[0]    v6:[3]    v7:[0]    
    v7 enqueue
    Indegree:
    v1:[0]    v2:[0]    v3:[0]    v4:[0]    v5:[0]    v6:[2]    v7:[0]    
    v3 enqueue
    v7 dequeue
    v3 dequeue
    Indegree:
    v1:[0]    v2:[0]    v3:[0]    v4:[0]    v5:[0]    v6:[0]    v7:[0]    
    v6 enqueue
    v6 dequeue
    拓扑排序:(格式为:结点名称:结点id:排序序号)
    v1:0:1
    v2:1:2
    v3:2:6
    v4:3:4
    v5:4:3
    v6:5:7
    v7:6:5
  • 相关阅读:
    最大生成树
    Codeforces#363 Div2
    AOJ2249最短路+最小费用
    Codeforces#364Div2
    POJ3268Dijkstra
    POJ3259负环判定
    Codeforces#362
    POJ3169差分约束系统
    POJ3723最小生成树
    hdu 4038 2011成都赛区网络赛H 贪心 ***
  • 原文地址:https://www.cnblogs.com/bigrabbit/p/2685970.html
Copyright © 2011-2022 走看看