[Data structure] Graph traversal (depth-first search, breadth-first search) and simple path problems (C language)

The traversal of the graph is to start from a certain vertex in the graph, and visit all the vertices in the graph in a certain way and only visit once. The graph traversal algorithm is the basis for solving graph connectivity problems, topological sorting and critical path algorithms.
In order to ensure that each vertex in the graph is visited only once during the traversal process, a visit flag needs to be set for each vertex, so an array of visit flags visited[n] should be set for the graph to indicate whether each vertex in the graph is visited visited.
For graph traversal, there are generally two methods, namely depth-first search and breadth-first search . These two traversal methods work for both undirected and directed graphs. Neither the depth-first search nor the breadth-first search results of a graph are unique .

1. Depth-first search

Depth-First Search (DFS) refers to searching in the direction of depth, which is similar to the root-first traversal of the tree, and is an extension of the root-first traversal of the tree.

1.1 Depth-first search steps

The basic steps of depth-first search :
① Starting from a certain vertex v0 in the graph, visit v0 first.
② Find out the first unvisited adjacent point of the vertex just visited, and then visit the vertex. Taking this vertex as a new vertex, repeat this step until the vertex just visited has no unvisited adjacent points.
③ Return to the previously visited vertex that still has unvisited adjacent points, find out the next unvisited adjacent point of this vertex, and visit this vertex; then execute step ②.
Example :
Depth-first search process diagram

Visit A ​​first, start from A;
(1) The adjacent points that A has not visited are B, D, E, visit B first; (
2) The adjacent points that B has not visited are C, D, visit C first;
(3) C The unvisited adjacent point is F, visit F;
(4) F has no unvisited adjacent point, backtrack to C;
(5) C has no unvisited adjacent point, backtrack to B;
(6) B has unvisited adjacent point D, visit D;
(7) The unvisited adjacent point of D is G, visit G;
(8) The unvisited adjacent points of G are E, H, visit E first;
(9) E has no unvisited adjacent point, Backtrack to G;
(10) The unvisited adjacent point of G is H, visit H;
(11) The unvisited adjacent point of H is I, visit I;
(12) I has no unvisited adjacent point, backtrack to H;
( 13) H has no unvisited adjacent points, backtrack to G;
(14) G has no unvisited adjacent points, backtrack to D;
(15) D has no unvisited adjacent points, backtrack to B;
(16) B has no unvisited adjacent points The adjacent points visited go back to A;
so far, the depth-first search is over, and the corresponding access sequence is: A, B, C, F, D, G, E, H, I; all vertices in the above figure are marked with
black The sides of the arrows form a tree rooted at A, which is called a depth-first search tree .
Note : When a vertex has multiple unvisited adjacent points, the order of points selected as the next visit may be different, so the depth-first search result of the graph is not unique !

1.2 Adjacency matrix method to realize depth-first search of connected graph

Implementing Depth-First Search of Connected Graphs Using Adjacency Matrix

/*用邻接矩阵方式实现深度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };			//访问标志数组
void DepthFirstSearch(AdjMatrix G, int v0) {
    
    
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;							//置1表示已访问过
	for (int i = 0; i < G.vexnum; i++) {
    
    
		if (!visited[i] && G.arcs[v0][i].adj == 1)
			DepthFirstSearch(G, i);
	}
}

Complete implementation code

# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数

/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode {
    
    
	AdjType adj;							//无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];		//顶点向量
	ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵
	int vexnum, arcnum;						//图的顶点数和弧数
}AdjMatrix;

/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建无向图的邻接矩阵*/
int CreateAdjMatrix(AdjMatrix* G) {
    
    
	int i, j, k;
	VertexData v1, v2;
	printf("输入图的顶点数和弧数:");			//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	for (i = 0; i < G->vexnum; i++) {
    
    		//初始化邻接矩阵
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j].adj = 0;
	}
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)			//输入图的顶点
		scanf(" %c", &G->vertex[i]);
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点:", k + 1);
		scanf(" %c %c", &v1, &v2);			//输入一条弧的两个顶点
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j].adj = 1;				//建立对称弧
		G->arcs[j][i].adj = 1;
	}
}

/*用邻接矩阵方式实现深度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };			//访问标志数组
void DepthFirstSearch(AdjMatrix G, int v0) {
    
    
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;							//置1表示已访问过
	for (int i = 0; i < G.vexnum; i++) {
    
    
		if (!visited[i] && G.arcs[v0][i].adj == 1)
			DepthFirstSearch(G, i);
	}
}

int main() {
    
    
	AdjMatrix G;
	CreateAdjMatrix(&G);
	printf("\n深度优先搜索:");
	DepthFirstSearch(G, 0);
	return 0;
}

operation result
Running result 1

1.3 Adjacency list method realizes depth-first search of connected graph

Implementing Depth-First Search of Connected Graph Using Adjacency List

/*用邻接表方式实现深度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };			//访问标志数组
void DepthFirstSearch(AdjList G, int v0) {
    
    
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;							//置1表示已访问过
	ArcNode* p;
	p = G.vertex[v0].firstarc;
	while (p != NULL) {
    
    
		if (!visited[p->adjvex])
			DepthFirstSearch(G, p->adjvex);
		p = p->nextarc;
	}
}

Complete implementation code

/*图的邻接表表示法*/
typedef char VertexData;
//弧结点结构
typedef struct ArcNode {
    
    
	int adjvex;								//该弧指向顶点的位置
	struct ArcNode* nextarc;				//指向下一条弧的指针
	int info;								//与弧相关的信息
}ArcNode;
//表头结点结构
typedef struct VertexNode {
    
    
	VertexData data;						//顶点数据
	ArcNode* firstarc;						//指向该顶点的第一条弧的指针
}VertexNode;
//邻接表结构
typedef struct {
    
    
	VertexNode vertex[MAX_VERTEX_NUM];
	int vexnum, arcnum;						//图的顶点数和弧数
}AdjList;

AdjList G;

/*求顶点位置*/
int LocateVertex(AdjList* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k].data == v)
			break;
	}
	return k;
}

/*创建图的邻接表*/
int CreateAdjList(AdjList* G) {
    
    
	int i, j, k;
	VertexData v1, v2;
	ArcNode* p;
	printf("输入图的顶点数和弧数:");				//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++) {
    
    			//输入图的顶点,初始化顶点结点
		scanf(" %c", &(G->vertex[i].data));
		G->vertex[i].firstarc = NULL;
	}
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点:", k + 1);
		scanf(" %c %c", &v1, &v2);				//输入一条弧的两个顶点
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		p = (ArcNode*)malloc(sizeof(ArcNode));	//申请新弧结点
		p->adjvex = j;
		p->nextarc = G->vertex[i].firstarc;
		G->vertex[i].firstarc = p;
	}
}

/*用邻接表方式实现深度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };			//访问标志数组
void DepthFirstSearch(AdjList G, int v0) {
    
    
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;							//置1表示已访问过
	ArcNode* p;
	p = G.vertex[v0].firstarc;
	while (p != NULL) {
    
    
		if (!visited[p->adjvex])
			DepthFirstSearch(G, p->adjvex);
		p = p->nextarc;
	}
}

int main() {
    
    
	AdjList G;
	CreateAdjList(&G);
	printf("\n深度优先搜索:");
	DepthFirstSearch(G, 0);
	return 0;
}

operation result
Running result 2

2. Breadth-first search

Breadth-First Search (Breadth-First Search) refers to searching in the direction of breadth, which is similar to the hierarchical traversal of the tree, and is the promotion of the hierarchical traversal of the tree.

2.1 Breadth-first search steps

The basic steps of breadth-first search :
① Starting from a certain vertex v0 in the graph, visit v0 first.
② Visit each unvisited adjacent point of v0 in turn.
③ Visit each unvisited adjacent point of these adjacent points respectively. Repeat ③ until all vertices are visited.
Example :
Breadth-first search processVisit A ​​first, start from A;
(1) The unvisited adjacent points of A are B, D, E, visit B first; (
2) Visit the second unvisited adjacent point D of A;
(3) Visit A (4) Since B is visited
before D and E, visit B's unvisited neighboring point C; (
5) Since D is visited before E and C, visit D's Adjacent point G is not visited;
(6) Because E is visited before C and G, and E has no unvisited adjacent point, so visit the unvisited adjacent point F of C; (7) Because
G is visited before F, so visit G’s unvisited adjacent point H;
(8) Since F is visited before H, and F has no unvisited adjacent point, so visit H’s unvisited adjacent point I;
so far, the breadth-first search ends, and the corresponding access sequence is: A, B, D, E, C, G, F, H, I;
all the vertices in the above figure plus the edges marked with arrows form a tree rooted at A, which is called a breadth-first search tree .
Note : When a vertex has multiple unvisited adjacent points, the order of points selected as the next visit may be different, so the breadth- first search result of the graph is not unique !

2.2 Adjacency matrix method to realize breadth-first search connected graph

Breadth-first Search of Connected Graphs Using Adjacency Matrix

/*邻接矩阵实现广度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };				//访问标志数组
void BreadthFirstSearch(AdjMatrix G, int v0) {
    
    
	int queue[MAX_VERTEX_NUM];						//一维数组模拟队列操作
	int rear = 0, front = 0, v;						//模拟队尾指针和队头指针
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;
	queue[rear] = v0;								//模拟入队
	while (rear >= front) {
    
    
		v = queue[front];							//模拟取队头元素
		front++;									//模拟出队
		for (int i = 0; i < G.vexnum; i++) {
    
    
			if (G.arcs[v][i].adj == 1 && !visited[i]) {
    
    
				printf("%c ", G.vertex[i]);
				visited[i] = 1;
				rear++;
				queue[rear] = i;					//模拟入队
			}
		}
	}
}

Complete implementation code

# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数

/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode {
    
    
	AdjType adj;									//无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];				//顶点向量
	ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];	//邻接矩阵
	int vexnum, arcnum;								//图的顶点数和弧数
}AdjMatrix;

/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建无向图的邻接矩阵*/
int CreateAdjMatrix(AdjMatrix* G) {
    
    
	int i, j, k;
	VertexData v1, v2;
	printf("输入图的顶点数和弧数:");					//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	for (i = 0; i < G->vexnum; i++) {
    
    				//初始化邻接矩阵
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j].adj = 0;
	}
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)					//输入图的顶点
		scanf(" %c", &G->vertex[i]);
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点:", k + 1);
		scanf(" %c %c", &v1, &v2);					//输入一条弧的两个顶点
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j].adj = 1;						//建立对称弧
		G->arcs[j][i].adj = 1;
	}
}

/*邻接矩阵实现广度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };				//访问标志数组
void BreadthFirstSearch(AdjMatrix G, int v0) {
    
    
	int queue[MAX_VERTEX_NUM];						//一维数组模拟队列操作
	int rear = 0, front = 0, v;						//模拟队尾指针和队头指针
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;
	queue[rear] = v0;								//模拟入队
	while (rear >= front) {
    
    
		v = queue[front];							//模拟取队头元素
		front++;									//模拟出队
		for (int i = 0; i < G.vexnum; i++) {
    
    
			if (G.arcs[v][i].adj == 1 && !visited[i]) {
    
    
				printf("%c ", G.vertex[i]);
				visited[i] = 1;
				rear++;
				queue[rear] = i;					//模拟入队
			}
		}
	}
}

int main() {
    
    
	AdjMatrix G;
	CreateAdjMatrix(&G);
	printf("\n广度优先搜索:");
	BreadthFirstSearch(G, 0);
	return 0;
}

operation result
Running result 3

2.2 Adjacency list method to realize breadth-first search connected graph

Breadth-first Search of Connected Graph Using Adjacency List

/*邻接表实现广度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };				//访问标志数组
void BreadthFirstSearch(AdjList G, int v0) {
    
    
	int queue[MAX_VERTEX_NUM];						//一维数组模拟队列操作
	int rear = 0, front = 0, v;						//模拟队尾指针和队头指针
	ArcNode* p;
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;
	queue[rear] = v0;								//模拟入队
	while (rear >= front) {
    
    
		v = queue[front];							//模拟取队头元素
		front++;									//模拟出队
		p = G.vertex[v].firstarc;
		while (p != NULL) {
    
    
			if (!visited[p->adjvex]) {
    
    
				printf("%c ", G.vertex[p->adjvex]);
				visited[p->adjvex] = 1;
				rear++;
				queue[rear] = p->adjvex;			//模拟入队
			}
			p = p->nextarc;
		}
	}
}

Complete implementation code

# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数

/*图的邻接表表示法*/
typedef char VertexData;
//弧结点结构
typedef struct ArcNode {
    
    
	int adjvex;										//该弧指向顶点的位置
	struct ArcNode* nextarc;						//指向下一条弧的指针
	int info;										//与弧相关的信息
}ArcNode;
//表头结点结构
typedef struct VertexNode {
    
    
	VertexData data;								//顶点数据
	ArcNode* firstarc;								//指向该顶点的第一条弧的指针
}VertexNode;
//邻接表结构
typedef struct {
    
    
	VertexNode vertex[MAX_VERTEX_NUM];
	int vexnum, arcnum;								//图的顶点数和弧数
}AdjList;

AdjList G;

/*求顶点位置*/
int LocateVertex(AdjList* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k].data == v)
			break;
	}
	return k;
}

/*创建图的邻接表*/
int CreateAdjList(AdjList* G) {
    
    
	int i, j, k;
	VertexData v1, v2;
	ArcNode* p;
	printf("输入图的顶点数和弧数:");					//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++) {
    
    				//输入图的顶点,初始化顶点结点
		scanf(" %c", &(G->vertex[i].data));
		G->vertex[i].firstarc = NULL;
	}
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点:", k + 1);
		scanf(" %c %c", &v1, &v2);					//输入一条弧的两个顶点
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		p = (ArcNode*)malloc(sizeof(ArcNode));		//申请新弧结点
		p->adjvex = j;
		p->nextarc = G->vertex[i].firstarc;
		G->vertex[i].firstarc = p;
	}
}

/*邻接表实现广度优先搜索*/
int visited[MAX_VERTEX_NUM] = {
    
     0 };				//访问标志数组
void BreadthFirstSearch(AdjList G, int v0) {
    
    
	int queue[MAX_VERTEX_NUM];						//一维数组模拟队列操作
	int rear = 0, front = 0, v;						//模拟队尾指针和队头指针
	ArcNode* p;
	printf("%c ", G.vertex[v0]);
	visited[v0] = 1;
	queue[rear] = v0;								//模拟入队
	while (rear >= front) {
    
    
		v = queue[front];							//模拟取队头元素
		front++;									//模拟出队
		p = G.vertex[v].firstarc;
		while (p != NULL) {
    
    
			if (!visited[p->adjvex]) {
    
    
				printf("%c ", G.vertex[p->adjvex]);
				visited[p->adjvex] = 1;
				rear++;
				queue[rear] = p->adjvex;			//模拟入队
			}
			p = p->nextarc;
		}
	}
}

int main() {
    
    
	AdjList G;
	CreateAdjList(&G);
	printf("\n广度优先搜索:");
	BreadthFirstSearch(G, 0);
	return 0;
}

operation result
Running result 4

3. Simple paths in graphs

Find a simple path from vertex u to vertex v in the graph .
If the vertices in the vertex sequence representing the path are different, the path is called a simple path , and the simple path between two vertices in the graph is not unique.
simple path

Idea : Search for routes based on depth-first search . Set the pre array to record the search route. When traversing from the vertex vi to its adjacent point vj, set pre[j] to i, so as to output the path after the search is completed. The pre array can be used instead of the visited array, and pre[j]=-1 means that vj has not been visited.
full code

# include<stdio.h>
# define MAX_VERTEX_NUM 20			//最多顶点个数

/*图的邻接矩阵表示法*/
typedef int AdjType;
typedef char VertexData;
typedef struct ArcNode {
    
    
	AdjType adj;							//无权图用1或0表示是否相邻,带权图则为权值类型
}ArcNode;
typedef struct {
    
    
	VertexData vertex[MAX_VERTEX_NUM];		//顶点向量
	ArcNode arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM];//邻接矩阵
	int vexnum, arcnum;						//图的顶点数和弧数
}AdjMatrix;

AdjMatrix G;

/*求顶点位置*/
int LocateVertex(AdjMatrix* G, VertexData v) {
    
    
	int k;
	for (k = 0; k < G->vexnum; k++) {
    
    
		if (G->vertex[k] == v)
			break;
	}
	return k;
}

/*创建无向图的邻接矩阵*/
int CreateAdjMatrix(AdjMatrix* G) {
    
    
	int i, j, k;
	VertexData v1, v2;
	printf("输入图的顶点数和弧数:");		//输入图的顶点数和弧数
	scanf("%d%d", &G->vexnum, &G->arcnum);
	for (i = 0; i < G->vexnum; i++) {
    
    		//初始化邻接矩阵
		for (j = 0; j < G->vexnum; j++)
			G->arcs[i][j].adj = 0;
	}
	printf("输入图的顶点:");
	for (i = 0; i < G->vexnum; i++)			//输入图的顶点
		scanf(" %c", &G->vertex[i]);
	for (k = 0; k < G->arcnum; k++) {
    
    
		printf("输入第%d条弧的两个顶点:", k + 1);
		scanf(" %c %c", &v1, &v2);			//输入一条弧的两个顶点
		i = LocateVertex(G, v1);
		j = LocateVertex(G, v2);
		G->arcs[i][j].adj = 1;				//建立对称弧
		G->arcs[j][i].adj = 1;
	}
}

int pre[MAX_VERTEX_NUM];					//全局变量,下标对应图顶点,值表示该顶点的遍历顺序,-1表示未被遍历
/*路径输出函数*/
void Output_Path() {
    
    
	int temp[MAX_VERTEX_NUM];
	for (int i = 0; i < G.vexnum; i++) {
    
    
		if (pre[i] != -1)
			temp[pre[i]] = i;
	}
	for (int i = 0; i < G.vexnum; i++)
		printf("%c ", G.vertex[temp[i]]);
}

/*深度优先搜索一条简单路径*/
/*u:起始顶点 v:终止顶点 dep:搜索深度*/
int DFS_path(AdjMatrix G, int u, int v, int dep) {
    
    
	int j;
	for (j = 0; j < G.vexnum; j++) {
    
    
		if (G.arcs[u][j].adj == 1 && pre[j] == -1) {
    
    
			pre[j] = dep;				//dep表示遍历顺序
			if (j == v) {
    
    				//路径遍历完成,输出
				Output_Path();
				return 1;
			}
			else
				DFS_path(G, j, v, dep + 1);//进入下一层搜索,即寻找顶点j可行的邻接点
			pre[j] = -1;				//行不通,回溯
		}
	}
	return 0;
}

void one_path(AdjMatrix G, int u, int v) {
    
    
	int i;
	for (i = 0; i < G.vexnum; i++)
		pre[i] = -1;
	pre[u] = 0;							//表明初始顶点u已被访问且顺序为0
	DFS_path(G, u, v, 1);				//进行dep为1的搜索,即寻找顶点u可行的邻接点
}

int main() {
    
    
	CreateAdjMatrix(&G);
	printf("\n%c->%c一条简单路径为:", G.vertex[7], G.vertex[0]);
	one_path(G, 7, 0);
	return 0;
}

Operation results
Running result 5
Reference: Geng Guohua "Data Structure - Described in C Language (Second Edition)"

For more data structure content, follow my "Data Structure" column : https://blog.csdn.net/weixin_51450101/category_11514538.html?spm=1001.2014.3001.5482

Guess you like

Origin blog.csdn.net/weixin_51450101/article/details/122963267