Graph traversal (depth-first traversal, DFS)

1. Concept

The graph traversal operation starts from a certain vertex in the graph, and visits all vertices in the graph once and only once

(1) In the graph, the starting vertex for traversal is the vertex with the smallest number

(2) If a starting point cannot reach all vertices, multiple calls are made to access all vertices

(3) In order to avoid the traversal from falling into an infinite loop due to loops , an array of visited flags visited[n] is attached (where it corresponds to all vertex subscripts, set it to 1 if it has been visited; set it to 0 if it has not been visited )

(4) The numbers of all nodes start from 0

2. Ideas

(1) Visit vertex v;

(2) Select a vertex w from the unvisited adjacent points of v (the selection rule is to find the node with the smallest adjacent number) , and perform depth-first traversal starting from w;

(3) Repeat the above two steps until all vertices in the graph that have paths with v are visited.

For example, the picture below: (The picture comes from the handouts of the related course "Data Structure" by Mr. Lazy Cat)

pseudocode:

Ways to find your next access point:

(1) Adjacency matrix: Because the numbering order of the columns of the adjacency matrix is ​​from small to large, looking for the next access point can directly access the first non-zero element of the row corresponding to the current access point (w has not been accessed)

(2) Adjacency list: access the edge table corresponding to the point, and the premise is that all nodes in the edge table are arranged from small to large subscripts

3. Code implementation

3.1 Functions

(1) Depth-first traversal of the adjacency matrix

void DFSTraverse(int arc[][MAX_VERTEX], DataType *vertex, int *visited, int vertexNum,
                 int v) { //v是遍历的起始位置的编号
	static int flag = 0;
	if (flag == 0) {//第一次需要初始化
		initvertex(vertexNum, visited);
		flag = 1;
	}
	visit(vertex, v); //输出访问过的顶点信息
	visited[v] = 1;
	for (int i = 0; i < vertexNum; i++) { //因为是邻接矩阵,所以编号的顺序本来就是由小到大的.所以不用遍历找
		if (arc[v][i] != 0 && visited[i] == 0)
			DFSTraverse(arc, vertex, visited, vertexNum, i); //二维数组传参,调用时实参直接写数组名
	}
}

(2) Depth-first traversal of the adjacency list

void DFSTraverse(VertexNode *adjList, int *visited, int vertexNum, int v) {
	static int flag = 0;
	if (flag == 0) {//第一次需要初始化
		initvertex(vertexNum, visited);
		flag = 1;
	}
	visit(adjList, v); //输出访问过的顶点信息
	visited[v] = 1;
	ArcNode *p = adjList[v].firstEdge;
	while (p != NULL) {
		if (visited[p->adjvex] == 0)
			DFSTraverse(adjList, visited, vertexNum, p->adjvex);
		p = p->next;
	}
}
void initvertex(int vertexNum, int *visited) { //初始化visited函数
	for (int i = 0; i < vertexNum; i++)
		visited[i] = 0;
}

void visit(VertexNode *adjList, int v) { //输出访问过的顶点信息
	printf("%d ", adjList[v].vertex);//记得改变输出时要改变数据类型
}

void ranklist(VertexNode *adjList, int vertexNum) { //将边表进行升序的排序,便于遍历操作
	ArcNode *p, *q;
	int temp;
	for (int i = 0; i < vertexNum; i++) {
		p = adjList[i].firstEdge;
		q = p;
		while (p != NULL) {
			while (q != NULL) {
				if (p->adjvex > q->adjvex) {
					temp = p->adjvex;
					p->adjvex = q->adjvex;
					q->adjvex = temp;
				}
				q = q->next;
			}
			p = p->next;
		}
	}
}

3.2 Complete code and test

(1) Adjacency matrix

1) graph_adjacency matrix.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_VERTEX 10//最大的顶点个数
typedef int DataType;

//以下是无向图的定义
void MGraph(DataType *vertex, int arc[][MAX_VERTEX], int vertexNum, int arcNum) { //初始化构造图(邻接矩阵法)
	printf("请逐个输入顶点的内容:");
	DataType x;
	DataType vi, vj; //构建邻接矩阵时,一条边的两个结点编号
	for (int i = 0; i < vertexNum; i++) { //顶点数组赋值
		scanf("%d", &x);
		vertex[i] = x;
	}
	for (int i = 0; i < vertexNum; i++) //初始化邻接矩阵
		for (int j = 0; j < vertexNum; j++)
			arc[i][j] = 0;
	int count = 1;
	for (int i = 0; i < arcNum; i++) { //依次输入每一条边
		printf("请输入第%d条边依附的两个顶点的编号:", count++);
		scanf("%d %d", &vi, &vj); //输入该边依附的顶点的编号
		arc[vi][vj] = 1; //置有边标志
		arc[vj][vi] = 1;
	}

}

void printMGraph(DataType *vertex, int arc[][MAX_VERTEX], int vertexNum) { //输出
	printf("vertex:");
	for (int i = 0; i < vertexNum; i++) {
		printf("%d ", vertex[i]);
	}
	printf("\n");
	printf("arc:\n");
	for (int i = 0; i < vertexNum; i++) {
		for (int j = 0; j < vertexNum; j++) {
			if (j == vertexNum - 1)
				printf("%d\n", arc[i][j]);
			else
				printf("%d ", arc[i][j]);
		}
	}

}

int isLinked(int arc[][MAX_VERTEX], int i, int j) { //两顶点i,j是否有边相连,1是相连,0是不相连
	if (arc[i][j] == 1)
		return 1;
	else
		return 0;
}

int nodeDepth(int arc[][MAX_VERTEX], int index, int vertexNum) { //任意一顶点的度
	//无向图任意遍历行\列求和
	int count = 0;
	for (int i = 0; i < vertexNum; i++) {
		if (arc[index][i] == 1)
			count++;
	}
	return count;
}

void initvertex(int vertexNum, int *visited) { //初始化visited函数
	for (int i = 0; i < vertexNum; i++)
		visited[i] = 0;
}

void visit(DataType *vertex, int v) { //输出访问过的顶点信息
	printf("%d ", vertex[v]);

}

void DFSTraverse(int arc[][MAX_VERTEX], DataType *vertex, int *visited, int vertexNum,
                 int v) { //v是遍历的起始位置的编号
	static int flag = 0;
	if (flag == 0) {//第一次需要初始化
		initvertex(vertexNum, visited);
		flag = 1;
	}
	visit(vertex, v); //输出访问过的顶点信息
	visited[v] = 1;
	for (int i = 0; i < vertexNum; i++) { //因为是邻接矩阵,所以编号的顺序本来就是由小到大的.所以不用遍历找
		if (arc[v][i] != 0 && visited[i] == 0)
			DFSTraverse(arc, vertex, visited, vertexNum, i); //二维数组传参,调用时实参直接写数组名
	}
}

2) Graph test_adjacency matrix.c

#include "图_邻接矩阵.h"

main() {
	DataType vertex[MAX_VERTEX];//储存所有的顶点
	int arc[MAX_VERTEX][MAX_VERTEX];//邻接矩阵,结点间的连通关系
	int vertexNum, arcNum; //结点个数,边的个数
	printf("输入顶点个数:");
	scanf("%d", &vertexNum);
	printf("输入边个数:");
	scanf("%d", &arcNum);
	MGraph(vertex, arc, vertexNum, arcNum);
	printMGraph(vertex, arc, vertexNum);
	printf("测试判断两顶点是否相连,请输入两个顶点下标:");
	int i, j;
	scanf("%d %d", &i, &j);
	if (isLinked(arc, i, j) == 1)
		printf("相连!\n");
	else
		printf("不相连!\n");
	printf("测试求顶点的度,请输入一个顶点下标:");
	int index;
	scanf("%d", &index);
	printf("该顶点的度为:%d\n", nodeDepth(arc, index, vertexNum));
	printf("测试邻接矩阵的深度优先遍历:\n");
	int visited[vertexNum];//判断结点是否访问过,访问过设置1,未访问过为0
	int v;
	printf("请输入深度优先遍历的第一个结点编号:");
	scanf("%d", &v);
	printf("深度优先遍历序列:");
	DFSTraverse(arc, vertex, visited, vertexNum, v);

}

3) Test output

Test case: (undirected graph)

Output: (Pay attention to distinguish the content and number of the node, the node data starts from 1, and the node number starts from 0)

(2) Adjacency list

1) Figure_adjacency list.h

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_VERTEX 10//最大的顶点个数
typedef char DataType;

/*
当DataType为char时要特别注意输入时空格和换行符,分别利用getchar()
和fflush(stdin);来清空内存区
*/
typedef struct ArcNode { /*边表结点*/
	int adjvex;//边表数据域,即下标
	struct ArcNode *next; //指针域
} ArcNode;

typedef struct VertexNode { /*顶点表结点*/
	DataType vertex;//顶点的数据
	ArcNode *firstEdge; //指向储存该顶点所连接所有结点的边表
} VertexNode;
void initVertex(DataType *, int);

//以下是有向图的定义
void ALGraph(DataType *vertex, VertexNode *adjList, int vertexNum, int arcNum) { //初始化构造图(邻接表法)
	int vi, vj;
	int count = 0;
	initVertex(vertex, vertexNum);
	for (int i = 0; i < vertexNum; i++) { //初始化顶点表
		adjList[i].vertex = vertex[i];
		adjList[i].firstEdge = NULL;
	}
	for (int i = 0; i < arcNum; i++) {
		//输入边的信息储存在边表中
		printf("请输入第%d条边依附的两个顶点的编号(方向->):", count++);
		fflush(stdin);//清除输入缓冲区(否则这里就会直接跳过scanf)
/*当DataType为int时要去掉上面这个语句*/
		scanf("%d %d", &vi, &vj); //输入该边依附的顶点的编号
		ArcNode *s;
		s = (ArcNode *)malloc(sizeof(ArcNode));
		if (s != NULL) {
			s->adjvex = vj;
			s->next = adjList[vi].firstEdge; //头插法建立链表
			adjList[vi].firstEdge = s;
		} else
			printf("init error!\n");
	}
}

void initVertex(DataType *vertex, int vertexNum) {//输入函数
	printf("请逐个输入顶点的内容:");
	DataType x;
	for (int i = 0; i < vertexNum; i++) { //顶点数组赋值
		getchar();//吸收空格,当DataType为int时要去掉这个语句
		scanf("%c", &x);
		vertex[i] = x;
	}
}

void printALGraph(VertexNode *adjList, int vertexNum) {
	printf("vertex  firstEdge\n");
	ArcNode *p ;
	for (int i = 0; i < vertexNum; i++) {
		printf("%3c -->", adjList[i].vertex);
		p = adjList[i].firstEdge;
		while (p != NULL) {
			printf("%d -->", p->adjvex);
			p = p->next;
		}
		printf("NULL\n");
		printf("\n");
	}
}

int isLinked(VertexNode *adjList, int i, int j) { //两顶点i,j是否有边相连,1是相连,0是不相连
	ArcNode *p = adjList[i].firstEdge ;
	while (p != NULL) {
		if (p->adjvex == j)
			return 1;
		else
			p = p->next;
	}
	p = adjList[j].firstEdge;
	while (p != NULL) {
		if (p->adjvex == i)
			return 1;
		else
			p = p->next;
	}
	return 0;
}

int nodeDepth(VertexNode *adjList, int index, int vertexNum) { //任意一顶点的度
	int count = 0;
	ArcNode *p = adjList[index].firstEdge;
	while (p != NULL) {
		count++;
		p = p->next;
	}
	return count;
}

void freeArcNode(VertexNode *adjList, int vertexNum) {
	ArcNode *p;
	ArcNode *temp;
	for (int i = 0; i < vertexNum; i++) {
		p = adjList[i].firstEdge ;
		while (p != NULL) {
			temp = p;
			p = p->next;
			free(temp);
		}
	}
}

void initvertex(int vertexNum, int *visited) { //初始化visited函数
	for (int i = 0; i < vertexNum; i++)
		visited[i] = 0;
}

void visit(VertexNode *adjList, int v) { //输出访问过的顶点信息
	printf("%c ", adjList[v].vertex);//记得改变输出时要改变数据类型
}

void DFSTraverse(VertexNode *adjList, int *visited, int vertexNum, int v) {
	static int flag = 0;
	if (flag == 0) {//第一次需要初始化
		initvertex(vertexNum, visited);
		flag = 1;
	}
	visit(adjList, v); //输出访问过的顶点信息
	visited[v] = 1;
	ArcNode *p = adjList[v].firstEdge;
	while (p != NULL) {
		if (visited[p->adjvex] == 0)
			DFSTraverse(adjList, visited, vertexNum, p->adjvex);
		p = p->next;
	}
}

void ranklist(VertexNode *adjList, int vertexNum) { //将边表进行升序的排序,便于遍历操作
	ArcNode *p, *q;
	int temp;
	for (int i = 0; i < vertexNum; i++) {
		p = adjList[i].firstEdge;
		q = p;
		while (p != NULL) {
			while (q != NULL) {
				if (p->adjvex > q->adjvex) {
					temp = p->adjvex;
					p->adjvex = q->adjvex;
					q->adjvex = temp;
				}
				q = q->next;
			}
			p = p->next;
		}
	}
}

2) Graph test_adjacency list.c

#include "图_邻接表.h"

int main() {
	DataType vertex[MAX_VERTEX];//储存所有的顶点
	int vertexNum, arcNum; //结点个数,边的个数
	printf("输入顶点个数:");
	scanf("%d", &vertexNum);
	printf("输入边个数:");
	scanf("%d", &arcNum);
	VertexNode adjList[vertexNum];//顶点表
	ALGraph(vertex, adjList, vertexNum, arcNum);
	ranklist(adjList, vertexNum);
	printALGraph(adjList, vertexNum);
	printf("测试判断两顶点是否相连,请输入两个顶点下标:");
	int i, j;
	scanf("%d %d", &i, &j);
	if (isLinked(adjList, i, j) == 1)
		printf("相连!\n");
	else
		printf("不相连!\n");
	printf("测试求顶点的度,请输入一个顶点下标:");
	int index;
	scanf("%d", &index);
	printf("该顶点的度为:%d\n", nodeDepth(adjList, index, vertexNum));
	printf("测试邻接表的深度优先遍历:\n");
	int visited[vertexNum];//判断结点是否访问过,访问过设置1,未访问过为0
	int v;
	printf("请输入深度优先遍历的第一个结点编号:");
	scanf("%d", &v);
	printf("深度优先遍历序列:");
	DFSTraverse(adjList, visited, vertexNum, v);
	freeArcNode(adjList, vertexNum);
}

3) Test output

Test case: (directed graph)

Output: (When the node data is char type, pay attention to the reading of spaces and line breaks when inputting)

Beginner Xiaobai, welcome to correct me if I make mistakes!

Guess you like

Origin blog.csdn.net/m0_63223213/article/details/126600687