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

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

  邻接表的处理方法是这样的:(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 }
时间: 2024-10-12 19:57:29

浅谈数据结构之图的邻接表深度和广度优先遍历(九)的相关文章

数据结构(10) -- 图的邻接表存储

////////////////////////////////////////////////////////// //图的邻接表存储 ////////////////////////////////////////////////////////// #include <iostream> #include <stdlib.h> using namespace std; //图的邻接表表示法 #define MaxVertexNum 100 enum GraphType{DG,

图的邻接表表示、广度优先、深度优先搜索

图,就是我们在数据结构中学到的图,它是一种存储信息的结构.图是一类在实际应用中非常常见的数据结构,当数据规模大到一定程度时,如何对其进行高效计算即成为迫切需要解决的问题.最常见的大规模图数据的例子就是互联网网页数据,网页之间通过链接指向形成规模超过500 亿节点的巨型网页图.再如,Facebook 社交网络也是规模巨大的图,仅好友关系已经形成超过10 亿节点.千亿边的巨型图,考虑到Facebook 正在将所有的实体数据节点都构建成网状结构,其最终形成的巨型网络数据规模可以想见其规模.要处理如此规

数据结构之图的邻接表

1.邻接表的简介: 图的邻接矩阵存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构.邻接表由表头结点和表结点两部分组成,其中图中每个顶点均对应一个存储在数组中的表头结点.如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中.如词条概念图所示,表结点存放的是邻接顶点在数组中的索引.对于无向图来说,使用邻接表进行存储也会出现数据冗余,表头结点A所指链表中存在一个指向C的表结点的同时,表头结点C所指链表也会存在一个指向A的表结点.[1] 邻接

浅谈数据结构(一) 线性表 Lists

一.vector和list 线性结构中,比较重要的有 vector和list,这两个都是C++的标准模板库(C++ Standard Template Library)中的库文件. 访问操作,查找和删除 vector可以提供下标访问,即v[i]的方式,所以索引方便.然而如果要插入数据,尤其是在下标小的地方插入,需要把其后面所有的数据全部都往后移动一位,因此代价非常高.同样的,删除数据也是这样. list在C++中指的是双向链表,由于是指针访问,因此元素的插入和删除代价较小,只要改变几个指针即可.

数据结构学习笔记05图 (邻接矩阵 邻接表--&gt;BFS DFS)

数据结构之图 图(Graph) 包含 一组顶点:通常用V (Vertex) 表示顶点集合 一组边:通常用E (Edge) 表示边的集合 边是顶点对:(v, w) ∈E ,其中v, w ∈ V 有向边<v, w> 表示从v指向w的边(单行线) 不考虑重边和自回路 无向图:边是无向边(v, w) 有向图:边是有向边<v, w> 连通:如果从V到W存在一条(无向)路径,则称V和W是连通的 连通图(Connected Graph):如果对于图的任一两个顶点v.w∈V,v和w都是连通的,则称

数据结构之---C语言实现图的邻接表存储表示

// 图的数组(邻接矩阵)存储表示 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_NAME 3 // 顶点字符串的最大长度+1 #define MAX_VERTEX_NUM 20 typedef int InfoType; // 存放网的权值 typedef char VertexType[MAX_NAME]; // 字符串类型 typedef enum{DG, DN, AG

看数据结构写代码(36) 图的邻接表表示与实现

图的邻接表表示法,是为每一个顶点建立一个链表,链表里存放着相同弧尾的 弧的信息,这些链表顺序存放在数组中.下面是无向图g2的邻接表 邻接表 比 邻接矩阵 节省空间,同时 也带来一些操作上的 不便,例如 看 两个顶点是否 相邻,需要 遍历 链表,在 求 无向图顶点的度时,只需 遍历 顶点的链表,而 求 有向图 顶点的度 需要 遍历 整个图 查找 弧头 为这个顶点的 个数. 如果 不想这样做,可以 建立 逆邻接表,即 链表里 存放着 相同 弧头的 弧 的信息. 下一节 要说的 十字链表 类似于这种结

浅谈数据结构-二叉树

浅谈数据结构-二叉树 二叉树是树的特殊一种,具有如下特点:1.每个结点最多有两颗子树,结点的度最大为2.2.左子树和右子树是有顺序的,次序不能颠倒.3.即使某结点只有一个子树,也要区分左右子树. 一.特殊的二叉树及特点 1.斜树 所有的结点都只有左子树(左斜树),或者只有右子树(右斜树).这就是斜树,应用较少 2.满二叉树 所有的分支结点都存在左子树和右子树,并且所有的叶子结点都在同一层上,这样就是满二叉树.就是完美圆满的意思,关键在于树的平衡. 根据满二叉树的定义,得到其特点为: 叶子只能出现

图(邻接表)

我们先来看一个图 我们想将这个图的信息存储到邻接表中,我们需要一个数组保存节点信息,还要有一个节点用来保存与该节点相邻的节点信息. 1 typedef struct arc_node 2 { 3 int pos; 4 int distance; 5 struct arc_node * next; 6 } Arc_node;//保存Node节点的相邻节点信息 7 8 typedef struct node 9 { 10 node_type info; 11 Arc_node * next; 12