浅谈数据结构之图的邻接表深度和广度优先遍历

Posted 连城无忧

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈数据结构之图的邻接表深度和广度优先遍历相关的知识,希望对你有一定的参考价值。

  邻接矩阵是一种不错的图存储结构,但是我们发现,对于边数相对较少的图,这种结构是存在对存储空间的极大浪费的。我们知道,顺序存储结构存在预先分配内存可能造成空间浪费的问题,于是引出了链式存储的结构。同样的,我们也可以考虑对边或弧使用链式存储的方式来避免空间浪费的问题。因此,对于图的存储结构,我们同样引入了一种数组与链表相组合的存储方法,我们一般称之为邻接表。

  邻接表的处理方法是这样的:(1).图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过数组可以较容易的读取顶点的信息,更加方便;另外,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该顶点的边信息;(2).图中每个顶点Vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点Vi的边表,有向图则称为顶点Vi作为弧尾的出边表。

  对比图的深度优先遍历与广度优先遍历,我们发现,它们在时间复杂度上是一样的,不同之处仅仅在于对顶点的访问顺序不同,可见两者在全图遍历上是没有优劣之分的,只是视不同的情况选择不同的方式而已。不过深度优先遍历更适合于目标比较明确,以找到目标为主要目的的情况,而广度优先遍历更适合在不断扩大遍历范围时找到相对最优解的情况。

图的创建及邻接表的深度和广度优先遍历源程序代码如下所示:

  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 #define MAXSIZE 9             /* 存储空间初始分配量 */
 10 #define MAXEDGE 15
 11 #define MAXVEX 9
 12 #define INFINITY 65535
 13 
 14 typedef int Status;           /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
 15 typedef int Boolean;          /* Boolean是布尔类型,其值是TRUE或FALSE */
 16 
 17 typedef char VertexType;      /* 顶点类型应由用户定义 */   
 18 typedef int EdgeType;         /* 边上的权值类型应由用户定义 */
 19 
 20 /* 邻接矩阵结构 */
 21 typedef struct
 22 {
 23     VertexType vexs[MAXVEX];         /* 顶点表 */
 24     EdgeType arc[MAXVEX][MAXVEX];    /* 邻接矩阵,可看作边表 */
 25     int numVertexes, numEdges;       /* 图中当前的顶点数和边数 */ 
 26 }MGraph;
 27 
 28 /* 邻接表结构 */
 29 typedef struct EdgeNode        /* 边表结点 */ 
 30 {
 31     int adjvex;                /* 邻接点域,存储该顶点对应的下标 */
 32     int weight;                /* 用于存储权值,对于非网图可以不需要 */
 33     struct EdgeNode *next;    /* 链域,指向下一个邻接点 */ 
 34 }EdgeNode;
 35 
 36 typedef struct VertexNode      /* 顶点表结点 */ 
 37 {
 38     int in;                    /* 顶点入度 */
 39     char data;                 /* 顶点域,存储顶点信息 */
 40     EdgeNode *firstedge;      /* 边表头指针 */   
 41 }VertexNode, AdjList[MAXVEX];
 42 
 43 typedef struct
 44 {
 45     AdjList adjList; 
 46     int numVertexes,numEdges;         /* 图中当前顶点数和边数 */
 47 }graphAdjList,*GraphAdjList;
 48 
 49 /* 用到的队列结构与函数 */
 50 /* 循环队列的顺序存储结构 */
 51 typedef struct
 52 {
 53     int data[MAXSIZE];
 54     int front;               /* 头指针 */
 55     int rear;                /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
 56 }Queue;
 57 
 58 /* 初始化一个空队列Q */
 59 Status InitQueue(Queue *Q)
 60 {
 61     Q->front=0;
 62     Q->rear=0;
 63     return  OK;
 64 }
 65 
 66 /* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
 67 Status QueueEmpty(Queue Q)
 68 { 
 69     if(Q.front==Q.rear)     /* 队列空的标志 */
 70         return TRUE;
 71     else
 72         return FALSE;
 73 }
 74 
 75 /* 若队列未满,则插入元素e为Q新的队尾元素 */
 76 Status EnQueue(Queue *Q,int e)
 77 {
 78     if ((Q->rear+1)%MAXSIZE == Q->front)        /* 队列满的判断 */
 79         return ERROR;
 80     Q->data[Q->rear]=e;                 /* 将元素e赋值给队尾 */
 81     Q->rear=(Q->rear+1)%MAXSIZE;        /* rear指针向后移一位置, */
 82                                         /* 若到最后则转到数组头部 */
 83     return  OK;
 84 }
 85 
 86 /* 若队列不空,则删除Q中队头元素,用e返回其值 */
 87 Status DeQueue(Queue *Q,int *e)
 88 {
 89     if (Q->front == Q->rear)            /* 队列空的判断 */
 90         return ERROR;
 91     *e=Q->data[Q->front];               /* 将队头元素赋值给e */
 92     Q->front=(Q->front+1)%MAXSIZE;      /* front指针向后移一位置, */
 93                                         /* 若到最后则转到数组头部 */
 94     return  OK;
 95 }
 96 
 97 void CreateMGraph(MGraph *G)
 98 {
 99     int i, j;
100 
101     G->numEdges=15;
102     G->numVertexes=9;
103 
104     /* 读入顶点信息,建立顶点表 */ 
105     G->vexs[0]=A;
106     G->vexs[1]=B;
107     G->vexs[2]=C;
108     G->vexs[3]=D;
109     G->vexs[4]=E;
110     G->vexs[5]=F;
111     G->vexs[6]=G;
112     G->vexs[7]=H;
113     G->vexs[8]=I;
114 
115     for (i = 0; i < G->numVertexes; i++)        /* 初始化图 */
116     {
117         for ( j = 0; j < G->numVertexes; j++)
118         {
119             G->arc[i][j]=0;
120         }
121     }
122 
123     G->arc[0][1]=1;
124     G->arc[0][5]=1;
125 
126     G->arc[1][2]=1; 
127     G->arc[1][8]=1; 
128     G->arc[1][6]=1; 
129     
130     G->arc[2][3]=1; 
131     G->arc[2][8]=1; 
132     
133     G->arc[3][4]=1;
134     G->arc[3][7]=1;
135     G->arc[3][6]=1;
136     G->arc[3][8]=1;
137 
138     G->arc[4][5]=1;
139     G->arc[4][7]=1;
140 
141     G->arc[5][6]=1; 
142     
143     G->arc[6][7]=1; 
144 
145     for(i = 0; i < G->numVertexes; i++)
146     {
147         for(j = i; j < G->numVertexes; j++)
148         {
149             G->arc[j][i] =G->arc[i][j];
150         }
151     }
152 }
153  
154 /* 利用邻接矩阵构建邻接表 */
155 void CreateALGraph(MGraph G,GraphAdjList *GL)
156 {
157     int i,j;
158     EdgeNode *e;
159 
160     *GL = (GraphAdjList)malloc(sizeof(graphAdjList));
161 
162     (*GL)->numVertexes=G.numVertexes;
163     (*GL)->numEdges=G.numEdges;
164     for(i= 0;i <G.numVertexes;i++)         /* 读入顶点信息,建立顶点表 */   
165     {
166         (*GL)->adjList[i].in=0;
167         (*GL)->adjList[i].data=G.vexs[i];
168         (*GL)->adjList[i].firstedge=NULL;         /* 将边表置为空表 */
169     }
170     
171     for(i=0;i<G.numVertexes;i++)         /* 建立边表 */
172     { 
173         for(j=0;j<G.numVertexes;j++)
174         {
175             if (G.arc[i][j]==1)
176             {
177                 e=(EdgeNode *)malloc(sizeof(EdgeNode));
178                 e->adjvex=j;                              /* 邻接序号为j */                         
179                 e->next=(*GL)->adjList[i].firstedge;      /* 将当前顶点上的指向的结点指针赋值给e */
180                 (*GL)->adjList[i].firstedge=e;            /* 将当前顶点的指针指向e */   
181                 (*GL)->adjList[j].in++;
182                 
183             }
184         }
185     }
186 }
187 
188 Boolean visited[MAXSIZE];         /* 访问标志的数组 */
189 
190 /* 邻接表的深度优先递归算法 */
191 void DFS(GraphAdjList GL, int i)
192 {
193     EdgeNode *p;
194      visited[i] = TRUE;
195      printf("%c ",GL->adjList[i].data);        /* 打印顶点,也可以其它操作 */
196     p = GL->adjList[i].firstedge;
197     while(p)
198     {
199          if(!visited[p->adjvex])
200              DFS(GL, p->adjvex);               /* 对为访问的邻接顶点递归调用 */
201         p = p->next;
202      }
203 }
204 
205 /* 邻接表的深度遍历操作 */
206 void DFSTraverse(GraphAdjList GL)
207 {
208     int i;
209      for(i = 0; i < GL->numVertexes; i++)
210          visited[i] = FALSE;         /* 初始所有顶点状态都是未访问过状态 */
211     for(i = 0; i < GL->numVertexes; i++)
212          if(!visited[i])             /* 对未访问过的顶点调用DFS,若是连通图,只会执行一次 */ 
213             DFS(GL, i);
214 }
215 
216 /* 邻接表的广度遍历算法 */
217 void BFSTraverse(GraphAdjList GL)
218 {
219     int i;
220     EdgeNode *p;
221     Queue Q;
222     for(i = 0; i < GL->numVertexes; i++)
223            visited[i] = FALSE;
224     InitQueue(&Q);
225        for(i = 0; i < GL->numVertexes; i++)
226        {
227         if (!visited[i])
228         {
229             visited[i]=TRUE;
230             printf("%c ",GL->adjList[i].data);        /* 打印顶点,也可以其它操作 */
231             EnQueue(&Q,i);
232             while(!QueueEmpty(Q))
233             {
234                 DeQueue(&Q,&i);
235                 p = GL->adjList[i].firstedge;         /* 找到当前顶点的边表链表头指针 */
236                 while(p)
237                 {
238                     if(!visited[p->adjvex])           /* 若此顶点未被访问 */
239                      {
240                          visited[p->adjvex]=TRUE;
241                         printf("%c ",GL->adjList[p->adjvex].data);
242                         EnQueue(&Q,p->adjvex);        /* 将此顶点入队列 */
243                     }
244                     p = p->next;        /* 指针指向下一个邻接点 */
245                 }
246             }
247         }
248     }
249 }
250 
251 
252 int main(void)
253 {    
254     MGraph G;  
255     GraphAdjList GL;    
256     CreateMGraph(&G);
257     CreateALGraph(G,&GL);
258 
259     printf("\n1.图的邻接表的深度优先遍历为:");
260     DFSTraverse(GL);
261     
262     printf("\n2.图的邻接表的广度优先遍历为:");
263     BFSTraverse(GL);
264     
265     return 0;
266 }

 

以上是关于浅谈数据结构之图的邻接表深度和广度优先遍历的主要内容,如果未能解决你的问题,请参考以下文章

图的深度/广度优先遍历C语言程序

图的邻接表存储表示,图的深度优先和广度优先遍历

图的遍历:深度优先遍历,广度优先遍历

用c语言编程 1创建图的邻接矩阵和邻接表 2验证图的深度优先、广度优先遍历算法 3验证最短路径

以邻接多重表为存储结构,实现连通无向图的深度优先遍历和广度优先遍历。

图的深度优先遍历DFS和广度优先遍历BFS(邻接矩阵存储)超详细完整代码进阶版