7.3.4 邻接多重表
对于无向图,邻接表关注的重点是顶点,如果用来操作边,比较麻烦,例如删除某条边需要删除两个边表结点,如图:
因此仿照十字链表,改造边表结构定义了邻接多重表,结构如下:
ivex和jvex是与某条边依附的两个顶点在定点表中的下标。
ilink指向依附顶点ivex的下一条边。
jlink指向依附顶点jvex的下一条边。
- 构造过程
①先画顶点和边;
②用顶点的firstedge指向第一条边;
③用ilink或jlink指向下一条边;
- 与邻接表的差别:
同一条边邻接表用两个结点表示,而邻接多重表只用一个节点。
7.3.5 边集数组
- 边集数组由两个一维数组构成。一个存储顶点信息,另一个存储边的信息,这个边数组每个数据元素由一条边的起点下标(begin)、终点下标(end)和权(weight)组成。
- 边集数组关注的是边的集合。在边集数组中查找顶点的度需要扫描整个边集数组,效率不高。
- 具体内容见7.5.2克鲁斯卡尔(Kruskal)算法。
7.4 图的遍历
7.4.1 深度优先遍历(递归)
适合目标比较明确,已找到目标位主要目的
- 邻接矩阵,深度优先遍历代码如下:
typedef int Boolean; /*Boolean是布尔类型,其值是TRUE或FALSE*/
Boolean visitied[MAX]; /*访问标志的数组*/
/*邻接矩阵的深度优先递归算法*/
void DFS(MGraph G,int i)
{
int j;
visited[i]= TRUE;
printf("%c",G.vexs[i]); /*打印顶点,也可以其他操作*/
/*从图结构G的顶点数组(vexs)中取出第i个元素并打印出来*/
核 for(j=0;j<G.numVertexes;j++) /*j从0到图中的顶点数(numVertexes)加1*/
核 if (G.arc[i][j]== 1 && !visited[j]) /*在邻接矩阵arc中值为1表示有边,值为0表示无边*/
核 DFS(G,j); /*对未访问的邻接顶点递归调用,注意这里使用的是*/
/*下一个未访问的邻接顶点j而不是i。*/
}
/*邻接矩阵的深度遍历操作*/
void DFSTracerse(MGraph G)
{
int i;
for (i= 0;i<G.numVertexes;i++)
visited[i]=FALSE; /*初始所有顶点状态都是未访问过状态*/
for(i = 0;i<G.numVertexes;i++)
if (!visited[i]) /*对未访问过的顶点调用DFS,若是连通图,只会执行一次*/
DFS(G,i);
}
- 邻接表结构,深度优先遍历代码如下:
typedef int Boolean; /*Boolean是布尔类型,其值是TRUE或FALSE*/
Boolean visitied[MAX]; /*访问标志的数组*/
/*邻接表的深度优先递归算法*/
void Dfs(GraphAdjList GL,int i)
{
EdgeNode *p; /*定义了一个EdgeNode类型的指针p*/
visited[i]=TRUE;
printf("%c",GL->adjlist[i].data);/*打印顶点,也可以其他操作*/
/*GL->adjlist[i].data指的是在邻接表中的第i个顶点的数据*/
p=GL->adjList[i].firstedge;
/*将指针p设置为指向邻接表GL中第i个顶点的第一个边*/
while(p)
{
核 if (!visited[p->adjvex]) /*adjvex为邻接点域,存储顶点对应的下标*/
核 DFS(GL,p->adjvex);/*对未访问的邻接顶点递归调用*/
核 p=p->next;
}
}
/*邻接表的深度遍历操作*/
void DFSTraverse(GraphAdjList GL)
{
int i;
for(i=0;i<GL->numVertexes;i++)
visited[i]=FALSE;/*初始所有顶点状态都是未访问过状态*/
for(i=0;i<GL->numVertexes;i++)
if (!visited[i])/*对未访问过的顶点调用DFS,若是连通图,只会执行一次*/
DFS(GL,i);
}
- 邻接矩阵 O [ n 2 ] O[n^2] O[n2],邻接表 O [ n ] O[n] O[n]
- GraphAdjList是"Graph Adjacency List"的缩写。
7.4.2 广度优先遍历(队列)
核心是层序遍历,更适合在不断扩大遍历范围时找到相对最优解的情况。
-
邻接矩阵结构,广度优先遍历代码如下:
/*邻接矩阵的广度遍历算法*/ void BFSTraverse(MGraph G) { int i,j; Queue Q; for (i=0;i<G.numVertexes;i++) visited[i]=FALSE; InitQueue(&Q); /*初始化一辅助用的队列*/ for(i=0;i<G.numVertexes;i++) /*对每一个顶点做循环*/ { if(!visited[i]) /*若是未访问过就处理*/ { visited[i]=TRUE; /*设置当前顶点访问过*/ printf("%c",G.vexs[i]); /*打印顶点,也可以其他操作*/ EnQueue(&Q,i); /*将此顶点入队列*/ while(!QueueEmpty(Q)) /*若当前队列不为空*/ { DeQueue(&Q,&i); /*将队中元素出队列,赋值给i*/ for(j=0;j<G.numVertexes;j++) { /*判断其他顶点若与当前顶点存在边且未访问过*/ if(G.arc[i][j]==1&&!visited[j]) { visited[j]=TRUE; /*将找到的此顶点标记为已访问*/ printf("%c",G.vexs[j]); /*打印顶点*/ EnQueue(&Q,j); /*将找到的此顶点入队列*/ } } } } } }
上述代码逻辑如下: 1.定义一个名为BFSTraverse的函数,它接受一个名为G的图作为参数。这个图应该包含顶点数numVertexes、顶点数组vexs和邻接矩阵arc。 2.初始化一个队列Q和一个访问状态数组visited,所有元素初始化为FALSE。 3.通过循环,遍历图中的每一个顶点。 4.对于每个未被访问过的顶点,做以下操作: 标记该顶点为已访问。 打印该顶点的名称或数据。 将该顶点加入队列Q。 5.当队列不为空时,进行以下循环: 从队列中删除并返回队首元素,并将删除的元素的索引赋值给i。 遍历所有顶点,对于每个与当前顶点i存在边且未被访问过的顶点j,做以下操作: 标记顶点j为已访问。 打印顶点j的名称或数据。 将顶点j加入队列Q。 6.重复步骤5,直到队列为空。 7.结束函数定义。 总的来说,这个函数实现了基于邻接矩阵的图的广度优先搜索算法。首先,它初始化所有顶点为未访问状态,然后对于每个未访问的顶点,将其标记为已访问并打印其名称或数据,并将其加入队列中。然后,依次处理队列中的顶点,对于每个顶点,将其未访问的相邻顶点标记为已访问并打印其名称或数据,并将其加入队列中。重复此过程,直到队列为空。
-
邻接表结构,广度优先遍历代码如下:
/*邻接表的广度遍历算法*/
void BFSTraverse (GraphAdjList GL)
{
int i;
EdgeNode *p;
Queue Q;
for(i=0;i<GL->numvertexes;i++)
visited[i]= FALSE;
InitQueue(&Q);
for(i=0;i<GL->numvertexes;i++)
{
if(!visited[i])
{
visited[i]=TRUE;
printf("%c",GL->adjList[i].data); /*打印顶点,也可以其他操作*/
EnQueue(&Q,i);
while(!QueueEmpy(Q))
{
DeQueueEmpty(&Q,&i);
p=GL->adjList[i].firstedge; /*找到当前顶点边表链表表头指针*/
while(p)
{
if(!visited[p->adjvex]) /*若此顶点未被访问*/
{
visited[p->adjvex]=TRUE;
printf("%c",GL->adjList[p->adjvex].data);
EnQueue(&Q,p->adjvex); /*将此顶点入队列*/
}
p=p->next; /*指针指向下一个邻接点*/
}
}
}
}
}