邻接表
邻接矩阵用二维数组即可存取,比较简单,但除完全图外,一般的图不是任意两个顶点都相邻接,因此邻接矩阵也有很多零元素,特别是当n 较大而边数相对完全图的边(n-1)又少得多时,邻接矩阵仍是很稀疏,这样浪费存储空间。
邻接表(Adjacency List)是图的一种顺序存储与链式存储结合的存储方法,类似于树的孩子链表表示法。由于它只考虑非零元素,因而节省了零元素所占的存储空间。它对于无向图和有向图都适用。
邻接表示法就是对于图G中的每个顶点放到一个数组中,数组的每个元素存放一个结点并指向一个单链表的指针。链表中存储着与该顶点相邻接的顶点所在的数组元素的下标。在邻接表表示中有两种结点结构,如图6-9所示。
(a) 表头结点 (b) 边表结点
图6-9 邻接矩阵表示的结点结构
在邻接表中,对图中每个顶点建立一个单链表。单链表有一个表头结点,表头结点的结构为图6-9(a)所示。其中,vertex域存放图中某个顶点vi 的信息,link为指针,指向对应单链表中的结点。
单链表中的结点称为边表结点,边表结点结构如图6-9(b)所示。其中,adjvex域存放与顶点vi相邻接的顶点在二维数组中的序号,next域为指针,指向与顶点vi相邻接的下一个顶点的边表结点。
下图6-10给出无向图6-7对应的邻接表表示。
图6-10 图的邻接表表示
拓扑排序
拓扑排序是对有向无环图的一种排序。表示了顶点按边的方向出现的先后顺序。如果有环,则无法表示两个顶点的先后顺序。
通常我们把计划、施工过程、生产流程、程序流程等都当成一个工程,一个大的工程常常被划分成许多较小的子工程,这些子工程称为活动。这些活动完成时,整个工程也就完成了。
我们用一种有向图来表示这些工程、计划等,在这种有向图中,顶点表示活动,有向边表示活动的优先关系,这种用顶点表示活动,用弧来表示活动间的优先关系的有向图叫做顶点表示活动的网络(Actire On Vertices)简称为AOV网。
拓扑排序:
假设G=(V,E)是一个具有n个顶点的有向图,V中顶点序列vl,v2,…,vn称做一个拓扑序列(Topological Order),当且仅当该顶点序列满足下列条件:若在有向图G中存在从顶点vi到vj的一条路径,则在顶点序列中顶点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:

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

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 }
按照上图拓扑顺序输入数据:

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