本博客为考试服务,研究邻接表表示法下BFS的实现。
图的定义以及邻接矩阵表示法,请参见C语言入门——DFS图的深度优先遍历-CSDN博客
涉及到的数据结构知识点:单链表、队列、图
一、图的邻接表表示法
图不仅可以用邻接矩阵表示,还可以用邻接表表示。
对于一个节点来说,如果我们只需要关注“与它有边相连”的节点,我们可以使用邻接表,将所有与之有边的关系的节点存入到一个单链表之中。
比如,对于这个图
按照上面的方法,可以得到下面的邻接表。
于是,图的边可以表示成一个List*的单链表数组。
typedef struct LNode* List;
struct LNode{
int element;
List next;
}LNode;//单链表
typedef GraphNode* Graph;
struct GraphNode{
List* Adjacency_List;//邻接表
Elementtype* data;//每个节点的数据
int Capacity;//节点的数量
}GraphNode;
二、广度优先遍历
广度优先遍历,就是在遇到一个节点时,访问所有的与其相邻的节点,这样一层一层“铺开”,用类似水波扩散的层序遍历的思想来访问所有的节点。这时,我们已经无法用递归来解决问题,因为“递归”本身就是一个具有“深度”属性的过程。
那怎么办?我们可以维护一个队列,首先,将起点入队。在队列未空的情况下,将队首元素弹出队列并访问队首元素,同时将没有入队过且与队首元素有边相连的元素入队,实现一次“扩散”。重复这个过程,直到队列空。这时,所有的元素都已经被访问,遍历结束。
和深度优先遍历一样,我们同样需要一个数组visited,记录节点有没有入队过。
bool visited[NodesSize];
memset(visited,0,NodesSize);
我们用这个图作为例子,研究广度优先的过程:我们不妨从A开始遍历。
首先,A入队,队列为{ A }
然后,A出队,B,D,E三个节点与A相连且没有入队过。将它们依次入队。队列变为{ E D B }
然后,队首元素B出队,A,D两个节点和B相连但都已经入过队。队列变为{E D}
然后,队首元素D出队,ABC三个节点与D相连,只有C没有入过队。入队C,队列变为{ C E }
接着,队首元素E出队,与其相连的节点A与E相连,但A已经入过队。队列变为{ C }
最后,C出队,与其相连节点D已经入过队,队列空,遍历结束。
至此,我们已经掌握广度优先思想的关键。
下面是BFS的C代码实现
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
typedef struct LNode* List;
struct LNode{
int element;
List next;
}LNode;//链表的结构体定义 :两个域:表的节点代号、指针域
typedef struct Queue* PtrQ;
struct Queue{
List front;
List rear;
}Queue;//队列的结构体定义
typedef struct GraphNode* Graph;
struct GraphNode{
List* Adjacency_List;
int* data;
int Capacity;
}GraphNode;//图的结构体定义:三个域:邻接表、数据、节点个数(图的容量)
void Add_to_end(List PtrL,int value){
if(PtrL==NULL) return;
if(PtrL->next==NULL){
PtrL->next=(List)malloc(sizeof(LNode));
PtrL->next->element=value;
PtrL->next->next=NULL;
}
else{
List tmp=PtrL->next;
List newl=(List)malloc(sizeof(LNode));
newl->element=value;
newl->next=tmp;
PtrL->next=newl;
}
}//链表函数:在链表头插入元素
Graph NewGraph(int NodesSize){
if(NodesSize<=0) return NULL;
Graph G=(Graph)malloc(sizeof(GraphNode));
G->Adjacency_List=(List*)malloc(sizeof(List)*NodesSize);
for(int i=0;i<NodesSize;i++){
G->Adjacency_List[i]=(List)malloc(sizeof(LNode));
G->Adjacency_List[i]->element=-1;
G->Adjacency_List[i]->next=NULL;
}
G->Capacity=NodesSize;
G->data=(int*)malloc(sizeof(int)*NodesSize);
for(int i=0;i<NodesSize;i++){
scanf("%d",&G->data[i]);
}
int relationshipSize;
scanf("%d",&relationshipSize);
if(relationshipSize<0) return G;
for(int i=0;i<relationshipSize;i++){
int Node1,Node2;
scanf("%d %d",&Node1,&Node2);
if(Node1>=0&&Node2>=0&&Node1<NodesSize&&Node2<NodesSize&&Node1!=Node2){
Add_to_end(G->Adjacency_List[Node1],Node2);
Add_to_end(G->Adjacency_List[Node2],Node1);
}
}
return G;
}//新建一个表,输入所有关系
PtrQ NewQueue(){
PtrQ Q=(PtrQ)malloc(sizeof(Queue));
Q->front=Q->rear=NULL;
return Q;
}//新建队列
void EnQueue(PtrQ Q,int value,bool* visited){
if(Q==NULL) return;
visited[value]=true;
if(Q->front==NULL){
Q->front=(List)malloc(sizeof(LNode));
Q->front->element=value;
Q->front->next=NULL;
Q->rear=Q->front;
return;
}
Q->rear->next=(List)malloc(sizeof(LNode));
Q->rear->next->element=value;
Q->rear->next->next=NULL;
Q->rear=Q->rear->next;
return;
}//入队
void DeQueue(PtrQ Q,Graph G,bool* visited){
if(Q->front==NULL) return;
int tmp=Q->front->element;
printf("%d ",G->data[tmp]);
if(Q->front==Q->rear){
free(Q->front);
Q->front=NULL;
Q->rear=NULL;
}
else{
List s=Q->front;
Q->front=s->next;
free(s);
s=NULL;
}//将队首元素弹出
List L=G->Adjacency_List[tmp]->next;
while(L!=NULL){
if(visited[L->element]==false)
EnQueue(Q,L->element,visited);
L=L->next;
}//与节点相连且没有入过队的节点入队
}
void BFS(Graph G,bool* visited,PtrQ Q,int place){
if(place<0&&place>=G->Capacity) return;
visited[place]=true;
EnQueue(Q,place,visited);
while(Q->front!=NULL){
DeQueue(Q,G,visited);
}
}//BFS广度优先
int main(){
int NodesSize;
scanf("%d",&NodesSize);
if(NodesSize<=0) return -0x7fffffff;//非法输入直接返回
Graph G=NewGraph(NodesSize);
PtrQ Q=NewQueue();//维护队列
bool visited[NodesSize];
memset(visited,0,NodesSize);//建立bool visited数组
BFS(G,visited,Q,0);
//记得在此还回申请的内存。
}