图:是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。注意:(1).线性表中我们把数据元素叫元素,树中将数据元素叫结点,而图中数据元素,我们一般称之为顶点;(2).在图结构中,不允许没有顶点,在定义中,若V是顶点的集合,则强调了顶点集合V有穷非空;(3).线性表中,相邻的元素之间具有线性关系,树结构中,相邻两层的结点具有层次关系,而图中,任意两个顶点之间都可能有关系,顶点之间的逻辑关系用边来表示,边的集合可以是空的。
图按照有无方向分为有向图和无向图,无向图由顶点和边构成,有向图由顶点和弧构成,弧有弧头和弧尾之分。由于图的结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据元素在内存中的物理位置来表示元素之间的关系,也就是说,图不可能用简单的顺序存储结构来表示,而多重链表的的方式,即以一个数据域和多个指针域组成的结点表示图中的一个顶点,尽管可以实现图结构,但还是有很多问题的。
考虑到图是由顶点和边或弧两部分组成的,而边或弧又是顶点与顶点之间的关系,所以可以用两个数组来表示图,一个以为数组存储图中顶点的信息,另一个二维数组存储图中边或弧的信息,其中这个二维数组我们一般称之为邻接矩阵。
图的遍历和树的遍历类似,是从图中某一顶点出发访遍其余顶点,且使每一个顶点仅被访问一次,这一过程就叫做图的遍历。图的遍历又分为深度优先遍历和广度优先遍历,深度优先遍历像是一棵树的前序遍历,而广度优先遍历就类似于一棵树的层序遍历。
图的创建及邻接矩阵深度和广度优先遍历源程序代码如下所示:
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define OK 1 5 #define ERROR 0 6 #define TRUE 1 7 #define FALSE 0 8 9 typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */ 10 typedef int Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */ 11 12 typedef char VertexType; /* 顶点类型应由用户定义 */ 13 typedef int EdgeType; /* 边上的权值类型应由用户定义 */ 14 15 #define MAXSIZE 9 /* 存储空间初始分配量 */ 16 #define MAXEDGE 15 17 #define MAXVEX 9 18 #define INFINITY 65535 19 20 typedef struct 21 { 22 VertexType vexs[MAXVEX]; /* 顶点表 */ 23 EdgeType arc[MAXVEX][MAXVEX]; /* 邻接矩阵,可看作边表 */ 24 int numVertexes, numEdges; /* 图中当前的顶点数和边数 */ 25 }MGraph; 26 27 /* 循环队列的顺序存储结构 */ 28 typedef struct 29 { 30 int data[MAXSIZE]; 31 int front; /* 头指针 */ 32 int rear; /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */ 33 }Queue; 34 35 /* 初始化一个空队列Q */ 36 Status InitQueue(Queue *Q) 37 { 38 Q->front=0; 39 Q->rear=0; 40 return OK; 41 } 42 43 /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */ 44 Status QueueEmpty(Queue Q) 45 { 46 if(Q.front==Q.rear) /* 队列空的标志 */ 47 return TRUE; 48 else 49 return FALSE; 50 } 51 52 /* 若队列未满,则插入元素e为Q新的队尾元素 */ 53 Status EnQueue(Queue *Q,int e) 54 { 55 if ((Q->rear+1)%MAXSIZE == Q->front) /* 队列满的判断 */ 56 return ERROR; 57 Q->data[Q->rear]=e; /* 将元素e赋值给队尾 */ 58 Q->rear=(Q->rear+1)%MAXSIZE; /* rear指针向后移一位置, */ 59 /* 若到最后则转到数组头部 */ 60 return OK; 61 } 62 63 /* 若队列不空,则删除Q中队头元素,用e返回其值 */ 64 Status DeQueue(Queue *Q,int *e) 65 { 66 if (Q->front == Q->rear) /* 队列空的判断 */ 67 return ERROR; 68 *e=Q->data[Q->front]; /* 将队头元素赋值给e */ 69 Q->front=(Q->front+1)%MAXSIZE; /* front指针向后移一位置, */ 70 /* 若到最后则转到数组头部 */ 71 return OK; 72 } 73 74 void CreateMGraph(MGraph *G) 75 { 76 int i, j; 77 78 G->numEdges=15; 79 G->numVertexes=9; 80 81 /* 读入顶点信息,建立顶点表 */ 82 G->vexs[0]='A'; 83 G->vexs[1]='B'; 84 G->vexs[2]='C'; 85 G->vexs[3]='D'; 86 G->vexs[4]='E'; 87 G->vexs[5]='F'; 88 G->vexs[6]='G'; 89 G->vexs[7]='H'; 90 G->vexs[8]='I'; 91 92 for (i = 0; i < G->numVertexes; i++) /* 初始化图 */ 93 { 94 for ( j = 0; j < G->numVertexes; j++) 95 { 96 G->arc[i][j]=0; 97 } 98 } 99 100 G->arc[0][1]=1; 101 G->arc[0][5]=1; 102 103 G->arc[1][2]=1; 104 G->arc[1][8]=1; 105 G->arc[1][6]=1; 106 107 G->arc[2][3]=1; 108 G->arc[2][8]=1; 109 110 G->arc[3][4]=1; 111 G->arc[3][7]=1; 112 G->arc[3][6]=1; 113 G->arc[3][8]=1; 114 115 G->arc[4][5]=1; 116 G->arc[4][7]=1; 117 118 G->arc[5][6]=1; 119 120 G->arc[6][7]=1; 121 122 for(i = 0; i < G->numVertexes; i++) 123 { 124 for(j = i; j < G->numVertexes; j++) 125 { 126 G->arc[j][i] =G->arc[i][j]; 127 } 128 } 129 } 130 131 Boolean visited[MAXVEX]; /* 访问标志的数组 */ 132 133 /* 邻接矩阵的深度优先递归算法 */ 134 void DFS(MGraph G, int i) 135 { 136 int j; 137 visited[i] = TRUE; 138 printf("%c ", G.vexs[i]); /* 打印顶点,也可以其它操作 */ 139 for(j = 0; j < G.numVertexes; j++) 140 if(G.arc[i][j] == 1 && !visited[j]) 141 DFS(G, j); /* 对为访问的邻接顶点递归调用 */ 142 } 143 144 /* 邻接矩阵的深度遍历操作 */ 145 void DFSTraverse(MGraph G) 146 { 147 int i; 148 for(i = 0; i < G.numVertexes; i++) 149 visited[i] = FALSE; /* 初始所有顶点状态都是未访问过状态 */ 150 for(i = 0; i < G.numVertexes; i++) 151 if(!visited[i]) /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */ 152 DFS(G, i); 153 } 154 155 /* 邻接矩阵的广度遍历算法 */ 156 void BFSTraverse(MGraph G) 157 { 158 int i, j; 159 Queue Q; 160 for(i = 0; i < G.numVertexes; i++) 161 visited[i] = FALSE; 162 InitQueue(&Q); /* 初始化一辅助用的队列 */ 163 for(i = 0; i < G.numVertexes; i++) /* 对每一个顶点做循环 */ 164 { 165 if (!visited[i]) /* 若是未访问过就处理 */ 166 { 167 visited[i]=TRUE; /* 设置当前顶点访问过 */ 168 printf("%c ", G.vexs[i]); /* 打印顶点,也可以其它操作 */ 169 EnQueue(&Q,i); /* 将此顶点入队列 */ 170 while(!QueueEmpty(Q)) /* 若当前队列不为空 */ 171 { 172 DeQueue(&Q,&i); /* 将队对元素出队列,赋值给i */ 173 for(j=0;j<G.numVertexes;j++) 174 { 175 /* 判断其它顶点若与当前顶点存在边且未访问过 */ 176 if(G.arc[i][j] == 1 && !visited[j]) 177 { 178 visited[j]=TRUE; /* 将找到的此顶点标记为已访问 */ 179 printf("%c ", G.vexs[j]); /* 打印顶点 */ 180 EnQueue(&Q,j); /* 将找到的此顶点入队列 */ 181 } 182 } 183 } 184 } 185 } 186 } 187 188 189 int main(void) 190 { 191 MGraph G; 192 CreateMGraph(&G); 193 194 printf(" 1.图的邻接矩阵的深度优先遍历为:"); 195 DFSTraverse(G); 196 197 printf(" 2.图的邻接矩阵的广度优先遍历为:"); 198 BFSTraverse(G); 199 200 return 0; 201 }