Depth/Breadth-First Traversal of Graphs DFS and BFS (Adjacency List Implementation) c language

content

definition

Adjacency List Storage Graph

depth-first traversal

Breadth-first traversal:

BFS section

full code


 

definition

To implement this algorithm, we must first know the concept of adjacency list.

The adjacency list is a commonly used graph storage structure. Its structural characteristics are:

Vertices are stored by a one-dimensional array;

Adjacent points are stored in a linked list

Compared with the adjacency matrix implemented purely with arrays, the adjacency list can avoid wasting space

Its diagram is as follows:

firstedge points to the first node of the edge list.

The value of adjvex in the edge table represents the vertex index that has an edge with the V0 vertex, for example:

The firstedge of A points to 1 of the edge table and then points to 2, 3, which means that A has a common edge with B, C, and D.

The time complexity of the algorithm: For a graph with n vertices and e edges, the time required to find adjacent points depends on the number of vertices and edges, so it is O(n+e)

 


Adjacency List Storage Graph

 

The code implementation of the adjacency list structure is as follows:

/*邻接表结构*/
typedef char VertexType;     //顶点类型 
typedef int EdgeType;		//权值类型 

/*边表结点*/ 
typedef struct EdgeNode
{
	int adjvex;      	 	//邻接点域,保存邻接点下标    
	EdgeType weight;		//存储权值,非网图则不需要 
	struct EdgeNode *next;	//链域,指向下一个邻接点 
}EdgeNode;

typedef struct VertexNode
{
	VertexType data;       	//顶点域 
	EdegeNode *firstedge;	//边表头指针	
}VertexNode,AdList[MAX];

typedef struct
{
	AdjList adjList;
	int numVertexes,numEdges;    //顶点数量和边数量 
}GraphAdjList,*GraphAdj; 

 

 

Create an adjacency list:

/*邻接表创建*/
void create(GraphAdj G)
{
	int i,j,k;
	EdgeNode *e;
	printf("输入顶点数,边数:");
	scanf("%d%d",&G->numVertexes,&G->numEdges);
	for(i=0;i<G->numVertexes;i++)          //建立顶点表 
	{
		scanf("%c",&G->adjList[i].data);
		G->adjList[i].firstedge=NULL; 		//注意将边表置空 
	}
	for(k=0;k<G->numEdges;k++)             //建立边表 
	{
		printf("输入边(Vi,Vj)上的顶点序号:");
		scanf("%d%d",&i,&j);
		/*使用头插法加入边表结点*/
		e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成边表结点 
		
		e->adjvex=j;
		e->next=G->adjList[i].firstedge;
		G->adjList[i].firstedge=e;
		
		e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成边表结点 
		
		e->adjvex=i;
		e->next=G->adjList[j].firstedge;
		G->adjList[j].firstedge=e;
				
	} 
	 
 } 

 


depth-first traversal

The depth-first traversal of the adjacency list is similar to the depth-first traversal of the adjacency matrix. It is necessary to create a flag array, and the array stores the bool type members TRUE, FALSE. where TRUE means it has been visited.

The difference: a variable p of type EdgeNode * needs to be declared in the recursive function to traverse the edge table, that is, to traverse the adjacent points in the linked list of the edge table.

 

 

/*邻接表的深度优先递归*/
void DFS(GraphAdj G,int i)
{
	EdgeNode *p;
	visited[i]=TRUE;         		//访问过了该顶点,标记为TRUE 
	printf("%c",G->adjList[i].data);
	p=G->adjList[i].firstedge;     //让p指向边表第一个结点 
	while(p)                      //在边表内遍历 
	{
		if(!visited[p->adjvex])    //对未访问的邻接顶点递归调用 
			DFS(G,p->adjvex);    
		p=p->next;
	}
 } 
 
 //邻接表的深度遍历操作
 
void DFSTraverse(GraphAdj G)
{
	int i;
	for(i=0;i<G->numVertexes;i++)
		visited[i]=FALSE;         //初始设置为未访问 
	for(i=0;i<G->numVertexes;i++)
		if(!visited[i])
			DFS(G,i);	//对未访问的顶点调用DFS,若是连通图只会执行一次 			
} 

 


 

Breadth-first traversal:

Implemented using a circular queue:

typedef struct LoopQueue{ //定义循环队列结构体 
	int data[MAX];
	int front;
	int rear;   //注意每次队尾标记指向最后一个元素的下一个位置 
}Queue, *LQueue; 

Various operations that require manual implementation of queues:

void InitQueue(LQueue &Q){  //初始化队列 
	Q->front = Q->rear = 0;
}

bool QueueisFull(LQueue &Q){ //判断队列是否满了 
	if((Q->rear + 1) % MAX == Q->front){
		return true;  //已满 
	}
	else{
		return false;
	}
}

bool QueueisEmpty(LQueue &Q){//判断队列是否为空 
	if(Q->front == Q->rear){
		return true;
	}
	return false;
}


void EnQueue(LQueue &Q, int i){ //入队列 
	if(!QueueisFull(Q)){
		Q->data[Q->rear] = i;
		Q->rear = (Q->rear + 1) % MAX;  //队尾指针后移 
	}
}

void DeQueue(LQueue &Q, int *k){ //出队列 
	if(!QueueisEmpty(Q)){
		*k = Q->data[Q->front];
		Q->front = (Q->front + 1) % MAX; 
	}
}

 

BFS section

The idea of ​​​​the algorithm is to start from the initial position vertex, traverse all adjacent vertices and add them to the queue. When all adjacent points of a vertex have been traversed, the vertex is dequeued, the next vertex is taken out, and then it is repeated. the process

 

/*广度优先遍历*/
void BFS(GraphAdj G){
	
	Queue *Q =(LQueue)malloc(sizeof(Queue));
	for(int i = 0; i < G->numVertexes; i++){
		visited[i] = FALSE;
	}
	InitQueue(Q);  //初始化队列 
	for(int i = 0; i < G->numVertexes; i++){
		visited[i] = TRUE;
		printf("\t%c", G->adjList[i].data);
		EnQueue(Q, i);
		
		while(!QueueisEmpty(Q)){
			DeQueue(Q, &i);  //这里不断的修改i的值!! 
			EdgeNode *e = G->adjList[i].firstedge;  //i顶点的邻接链表的第一个结点
			while(e){//e存在时,将e的所有邻接点加入队列,也就是遍历i的所有邻接点 
				if(!visited[e->adjvex]){ // adjvex是e所表示的结点下标 
					visited[e->adjvex] = TRUE;
					printf("\t%c", G->adjList[e->adjvex].data);
					EnQueue(Q, e->adjvex); //将该结点入队 
				}
				e = e->next; //遍历i的下一个邻接点 
			}
		} 
	}
} 

 


 

full code

 

#include<stdio.h>
#include<stdlib.h>
#define MAX 10
#define INIFINITY 65535
#define TRUE 1
#define FALSE 0
typedef int Boole;  //布尔类型 存储TRUE FALSE
Boole visited[MAX];    //访问标志数组 
 
//邻接表结点定义
typedef char VertexType;  //顶点数据类型	 
typedef int EdgeType;    //边上的权值类型 
 
typedef struct EdgeNode  //边表结点   存储边表信息 
{
	int adjvex;		    //邻接点域,存储该顶点对应的下标 
	EdgeType weight;	//权值 
	struct EdgeNode *next;	//链域,指向下一个邻接点 
}EdgeNode;
 
typedef struct VertexNode   //顶点表结点
{
	VertexType data;      //顶点域,存储顶点信息 
	EdgeNode *firstedge;	//边表头指针,指向此顶点的第一个邻接点 
}VertexNode,AdjList[MAX]; 
 
 
typedef struct
{
	AdjList adjList;     
	int numVertexes,numEdges;   //图中当前顶点数和边数 
}GraphAdjList,*GraphAdj;


typedef struct LoopQueue{ //定义循环队列结构体 
	int data[MAX];
	int front;
	int rear;   //注意每次队尾标记指向最后一个元素的下一个位置 
}Queue, *LQueue; 

void InitQueue(LQueue &Q){  //初始化队列 
	Q->front = Q->rear = 0;
}

bool QueueisFull(LQueue &Q){ //判断队列是否满了 
	if((Q->rear + 1) % MAX == Q->front){
		return true;  //已满 
	}
	else{
		return false;
	}
}

bool QueueisEmpty(LQueue &Q){//判断队列是否为空 
	if(Q->front == Q->rear){
		return true;
	}
	return false;
}


void EnQueue(LQueue &Q, int i){ //入队列 
	if(!QueueisFull(Q)){
		Q->data[Q->rear] = i;
		Q->rear = (Q->rear + 1) % MAX;  //队尾指针后移 
	}
}

void DeQueue(LQueue &Q, int *k){ //出队列 
	if(!QueueisEmpty(Q)){
		*k = Q->data[Q->front];
		Q->front = (Q->front + 1) % MAX; 
	}
}

 
/*邻接表创建*/
void create(GraphAdj G)
{
	int i,j,k;
	EdgeNode *e;
	printf("输入顶点数和边数:");
	scanf("%d%d",&G->numVertexes,&G->numEdges);
	getchar();  						//注意要清除缓冲 
	for(i=0;i<G->numVertexes;i++)          //建立顶点表 
	{
		scanf("%c",&G->adjList[i].data);    //输入顶点的符号 
		G->adjList[i].firstedge=NULL; 		//将边表置空 
		getchar();
	}
	for(k=0;k<G->numEdges;k++)             //建立边表 
	{
		printf("输入边(Vi,Vj)上的顶点序号:");
		scanf("%d%d",&i,&j);
		/*使用头插法加入边表结点*/
		e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 

		e->adjvex=j;
		e->next=G->adjList[i].firstedge;
		G->adjList[i].firstedge=e;
		
		e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 
		
		e->adjvex=i;
		e->next=G->adjList[j].firstedge;
		G->adjList[j].firstedge=e;			
	} 
	printf("\n");
} 
 
 
/*邻接表的深度优先递归*/
void DFS(GraphAdj G,int i)
{
	EdgeNode *p;
	visited[i]=TRUE;         		//访问过了该顶点,标记为TRUE 
	printf("\t%c",G->adjList[i].data);
	p=G->adjList[i].firstedge;     //让p指向边表第一个结点 
	while(p)                      //在边表内遍历 
	{
		if(!visited[p->adjvex])    //对未访问的邻接顶点递归调用 
			DFS(G,p->adjvex);    
		p=p->next;
	}
 } 
 
 //邻接表的深度遍历操作
 
void DFSTraverse(GraphAdj 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);				
} 
 
/*广度优先遍历*/
void BFS(GraphAdj G){
	
	Queue *Q =(LQueue)malloc(sizeof(Queue));
	for(int i = 0; i < G->numVertexes; i++){
		visited[i] = FALSE;
	}
	InitQueue(Q);  //初始化队列 
	for(int i = 0; i < G->numVertexes; i++){
		visited[i] = TRUE;
		printf("\t%c", G->adjList[i].data);
		EnQueue(Q, i);
		
		while(!QueueisEmpty(Q)){
			DeQueue(Q, &i);  //这里不断的修改i的值!! 
			EdgeNode *e = G->adjList[i].firstedge;  //i顶点的邻接链表的第一个结点
			while(e){//e存在时,将e的所有邻接点加入队列,也就是遍历i的所有邻接点 
				if(!visited[e->adjvex]){ // adjvex是e所表示的结点下标 
					visited[e->adjvex] = TRUE;
					printf("\t%c", G->adjList[e->adjvex].data);
					EnQueue(Q, e->adjvex); //将该结点入队 
				}
				e = e->next; //遍历i的下一个邻接点 
			}
		} 
	}
} 
 
int main()
{
	GraphAdjList G;
	create(&G);
	printf("深度优先遍历为:"); 
	DFSTraverse(&G);
	printf("\n");
	printf("广度优先遍历为:"); 
	BFS(&G);
	printf("\n图遍历完毕");
	return 0;	 
 } 

 

Error warning:

At the beginning what I wrote in the main function was

int main()
{
	GraphAdj G;
	create(G);
	printf("\n");
	DFSTraverse(G);
	printf("\n图遍历完毕");
	return 0;	 
 } 

 

However, there will be problems at runtime. Later I learned that GraphAdj G needs to be initialized, and the pointer variable is allocated memory space, that is, it must have a pointer before its value can be referenced

 

But if it is in cpp, you can change the formal parameters of the create function to:

GraphAdj &G

 

&的意思是传进来节点指针的引用,括号内等价于 GraphAdj* &G,目的是让传递进来的指针发生改变

 

After using the reference, the main function can be written in the above form.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326390573&siteId=291194637