算法导论 第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

 

 

 

转载于:https://www.cnblogs.com/bigrabbit/archive/2012/09/15/2685970.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值