蛮力算法之深度优先遍历和广度优先遍历——图的深度优先遍历和广度优先遍历,附带案例:迷宫问题及矩阵中传染性传播问题

算法:图的深度优先搜索和广度优先搜索

这两种搜索方法本质上都是基于蛮力法思路

这两种搜索方法对有向图和无向图都适用

1 图的两种定义方式

1.1 邻接矩阵

constexpr auto MAXV = 1000; //定义最大顶点个数

//顶点信息
typedef struct
{
	int no;
	char data[MAXV];
}VertexType;

//矩阵信息
typedef struct
{
	int edges[MAXV][MAXV];
	int n, e;
	VertexType vexs[MAXV];
}MGraph;

邻接矩阵示意图:

juz

1.2 邻接表

注意:边结点代表的是图中的边,而并非图中的结点;头结点才表示图中的结点;头结点身后所连接的为边结点

//边结点 
typedef struct ANode
{
	int adjvex;			//这条边的终点
	int weight;			//这条边的权重
	struct ANode* nextarc;  //指向下一条与该结点相邻的边
} ArcNode;


//头结点
typedef struct VNode
{
	string date;		//顶点的其他信息
	ArcNode* firstarc;	//指向第一条边
};


//邻接表
typedef struct
{
	VNode adjList[MAXV];  //头结点组成的数组
	int n, e;            //图中的顶点数n和边数e
}ALGraph;

邻接表示意图:(一般默认为出边表)

biao

2 图的深度优先遍历

关键:

  1. 用**visited[]**数组记录是否已访问
  2. 这是一个递归的过程

邻接矩阵的:

void DFS(MGraph g,int v)
{	int w;
	cout<<v<<" ";						//输出被访问顶点的编号
	visited[v]=1;						//置已访问标记
	for (w=0;w<g.n;w++)					//找顶点v的所有相邻点
		if (g.edges[v][w]!=0 && g.edges[v][w]!=INF && visited[w]==0)
			DFS(g,w);					//找顶点v的未访问过的相邻点w
}

邻接表的:

void DFS(ALGraph *G,int v)
{	ArcNode *p;
	cout<<v<<" ";					//输出被访问顶点的编号
	visited[v]=1;						//置已访问标记
	p=G->adjlist[v].firstarc;			//p指向顶点v的第一个邻接点
	while (p!=NULL)
	{	if (visited[p->adjvex]==0)		//若p->adjvex顶点未访问,递归访问它
			DFS(G,p->adjvex);
		p=p->nextarc;					//p指向顶点v的下一个邻接点
	}
}

3 图的广度优先遍历

关键:

  1. 使用数据结构 → 队列来记录未访问的邻接点
  2. 用**visited[]**数组记录哪些点已经访问了,哪些还没有
  3. 采用循环,循环终止条件为队列空了

邻接矩阵的:

void BFS(MGraph g,int v)					//邻接矩阵的DFS算法
{
	queue<int> qu;							//定义一个队列qu
	int visited[MAXV];						//定义存放结点的访问标志的数组
	int w,i;
	memset(visited,0,sizeof(visited));		//访问标志数组初始化

	cout<<v<<" ";						//输出被访问顶点的编号
	visited[v]=1;							//置已访问标记
	qu.push(v);								//v进队
	while (!qu.empty())						//队列不空时循环
	{
		w=qu.front(); qu.pop();				//出队顶点w
		for (i=0;i<g.n;i++)					//找与顶点w相邻的顶点
			if (g.edges[w][i]!=0 && g.edges[w][i]!=INF && visited[i]==0)
			{	//若当前邻接顶点i未被访问
				cout<<i<<" ";			//访问相邻顶点
				visited[i]=1;				//置该顶点已被访问的标志
				qu.push(i);					//该顶点进队
			}
	}
	printf("\n");
}

邻接表的:

void BFS(ALGraph *G,int v)					//邻接表的DFS算法
{
	ArcNode *p;
	queue<int> qu;							//定义一个队列qu
	int visited[MAXV];						//定义存放顶点的访问标志的数组
	int w;
	memset(visited,0,sizeof(visited));		//访问标志数组初始化
	cout<<v<<" ";						//输出被访问顶点的编号
	visited[v]=1; 							//置已访问标记
	qu.push(v);								//v进队
	while (!qu.empty())						//队列不空时循环
	{
		w=qu.front(); qu.pop();				//出队顶点w
		p=G->adjlist[w].firstarc;			//找顶点w的第一个邻接点
		while (p!=NULL) 
		{	if (visited[p->adjvex]==0)		//若当前邻接顶点未被访问
			{	cout<<i<<" ";	//访问相邻顶点
				visited[p->adjvex]=1;		//置该顶点已被访问的标志
				qu.push(p->adjvex);			//该顶点进队
			}
			p=p->nextarc;					//找顶点w的下一个邻接点
		}
	}
	printf("\n");
}

两种遍历方式都重点掌握邻接表的。

案例

案例1:迷宫

【问题描述】有如下8*8的迷宫图:

OXXXXXXX
OOOOOXXX
XOXXOOOX
XOXXOXXO
XOXXXXXX
XOXXOOOX
XOOOOXOO
XXXXXXXO

其中,O表示通路方块,X表示障碍方块。假设入口是位置(0,0),出口为右下角方块位置(7,7)。设计一个程序采用递归方法求指定入口到出口的一条迷宫路径。

深度优先的解法:

关键:

  1. 用数组记录垂直与水平偏移量,才能运用for循环遍历来尝试各种可能
  2. 采用递归,递归的终止条件为达到了终点
  3. 如何记录过程(路径)?在深度遍历的过程中,将可行的点作上了记号,不可行的点保留了原貌;最后输出矩阵即可
#include <stdio.h>
#define MAxN 10						//最大迷宫大小
//问题表示
int n=8;							//迷宫大小
char Maze[MAxN][MAxN]=
{	{'O','X','X','X','X','X','X','X'},
	{'O','O','O','O','O','X','X','X'},
	{'X','O','X','X','O','O','O','X'},
	{'X','O','X','X','O','X','X','O'},
	{'X','O','X','X','X','X','X','X'},
	{'X','O','X','X','O','O','O','X'},
	{'X','O','O','O','O','X','O','O'},
	{'X','X','X','X','X','X','X','O'}
};
int H[4] = {0, 1, 0, -1};			//水平偏移量,下标对应方位号0~3
int V[4] = {-1, 0, 1, 0};			//垂直偏移量

void disppath()						//输出一条迷宫路径
{
	for (int i=0; i<n;i++)
	{
		printf("    ");
		for(int j=0; j<n;j++)
			printf("%c",Maze[i][j]);
		printf("\n");
	}
}
void DFS(int x,int y)			 //求从(x,y)出发的一条迷宫路径
{
	if (x==n-1 && y==n-1) //找到一条路径,输出
	{
		Maze[n-1][n-1]=' ';
		disppath();
		return;
	}
	else
	{	for (int k=0;k<4;k++)				 //试探每一个方位
			if(x>=0 && y>=0 && x<n && y<n && Maze[x][y]=='O')
			{								//若(x,y)方块的可走的
				Maze[x][y]=' ';				//将该方块设置为空字符
				DFS(x+V[k],y+H[k]);			//查找(x,y)周围的每一个相邻方块
				Maze[x][y]='O';				//若从该相邻方块出发没有找到路径,恢复(x,y)
			}
	}
}
void main()
{
	int x=0,y=0;		//指定入口,出口默认为(n-1,n-1)
	printf("一条迷宫路径:\n");
	DFS(x,y);			//求(0,0)->(7,7)的一条迷宫路径
}

广度优先的解法:

关键:

  1. 用数组记录垂直与水平偏移量,才能运用for循环遍历来尝试各种可能
  2. 如何记录路径:采用队列,对于可行的方块,我们将其放入队列尾部(方块是用结构体定义的)
  3. 不会记录到多条路径吗?是的,队列中除了结果路径的点之外,还有广度遍历过程中其他路径上的点,所以,我们要用结构体来定义点,并赋予一个属性pre,用来记录这个点的前驱在队列中的下标(用数组加上队头队尾组成的队列支持随机访问)
  4. 在将方块添加到队列之前,要将其修改为某个特定的值防止其被再次访问到。例如A之后的B可行,接下来我们要以B为对象,判断它周围的方块是否可行,A也是其周围方块之一,但是不用再访问A了,因为之前已经访问过了
#include <stdio.h>
#define MAXQ 100					//队列大小
#define MAxN 10						//最大迷宫大小
//问题表示
int n=8;							//迷宫大小
char Maze[MAxN][MAxN]=
{	{'O','X','X','X','X','X','X','X'},
	{'O','O','O','O','O','X','X','X'},
	{'X','O','X','X','O','O','O','X'},
	{'X','O','X','X','O','X','X','O'},
	{'X','O','X','X','X','X','X','X'},
	{'X','O','X','X','O','O','O','X'},
	{'X','O','O','O','O','X','O','O'},
	{'X','X','X','X','X','X','X','O'}
};
int H[4] = {0, 1, 0, -1};			//水平偏移量,下标对应方位号0~3
int V[4] = {-1, 0, 1, 0};			//垂直偏移量
struct Position						//队列元素类型
{
	int x,y;						//当前方块位置
	int pre;						//前驱方块的下标
};
Position qu[MAXQ];					//定义一个队列qu
int front=-1,rear=-1;				//定义队头和队尾

void disppath(int front)			//输出一条迷宫路径
{
	int i,j;
	for (i=0; i<n;i++)				//将所有'*'改为'O'
		for (j=0;j<n;j++)
			if (Maze[i][j]=='*')
				Maze[i][j]='O';
	int k=front;
	while (k!=-1)					//即路径上的方块改为' '
	{
		Maze[qu[k].x][qu[k].y]=' ';
		k=qu[k].pre;
	}
	for (i=0; i<n;i++)				//输出迷宫路径
	{	printf("    ");
		for(int j=0; j<n;j++)
			printf("%c",Maze[i][j]);
		printf("\n");
	}
}

void BFS(int x,int y)			 //求从(x,y)出发的一条迷宫路径
{
	Position p,p1,p2;
	p.x=x; p.y=y; p.pre=-1;			//建立入口结点
	Maze[p.x][p.y]='*';				//改为'*'避免重复查找
	rear++; qu[rear]=p;				//入口方块进队
	while (front!=rear)				//队不空循环
	{
		front++; p1=qu[front];		//出队方块p1;
		if (p1.x==n-1 && p1.y==n-1)	//找到出口
		{
			disppath(front);		//输出路径
			return;
		}
		for (int k=0;k<4;k++)		//试探p1的每个相邻方位
		{
			p2.x=p1.x+V[k];			//找到p1的相邻方块p2
			p2.y=p1.y+H[k];
			if (p2.x>=0 && p2.y>=0 && p2.x<n && p2.y<n && Maze[p2.x][p2.y]=='O')
			{	//方块p2有效并且可走
				Maze[p2.x][p2.y]='*';	//改为'*'避免重复查找
				p2.pre=front;
				rear++;	qu[rear]=p2;	//方块p2进队
			}
		}
	}
}
void main()
{
	int x=0,y=0;		//指定入口,出口默认为(n-1,n-1)
	printf("一条迷宫路径:\n");
	BFS(x,y);			//求(0,0)->(7,7)的一条迷宫路径
}

案例2:传染

这个案例可以更好的理解广度优先遍历

小蓝有一个01矩阵。他打算将第一行第一列的 0 变为 2 。变化过程有传染性,每次 2 的上下左右四个相邻的位置中的 0 都会变成 2 。直到最后每个 2 的周围都是 1 或 2 结束。
请问,最终矩阵中有多少个 2 ?
以下是小蓝的矩阵,共 30 行 40 列。

0000100010000001101010101001001100000011
0101111001111101110111100000101010011111
1000010000011101010110000000001011010100
0110101010110000000101100100000101001001
0000011010100000111111001101100010101001
0110000110000000110100000000010010100011
0100110010000110000000100010000101110000
0010011010100110001111001101100110100010
1111000111101000001110010001001011101101
0011110100011000000001101001101110100001
0000000101011000010011111001010011011100
0000100000011001000100101000111011101100
0010110000001000001010100011000010100011
0110110000100011011010011010001101011011
0000100100000001010000101100000000000010
0011001000001000000010011001100101000110
1110101000011000000100011001001100111010
0000100100111000001101001000001010010001
0100010010000110100001100000110111110101
1000001001100010011001111101011001110001
0000000010100101000000111100110010101101
0010110101001100000100000010000010110011
0000011101001001000111011000100111010100
0010001100100000011000101011000000010101
1001111010010110011010101110000000101110
0110011101000010100001000101001001100010
1101000000010010011001000100110010000101
1001100010100010000100000101111111111100
1001011010101100001000000011000110110000
0011000100011000010111101000101110110001

思路:广度优先遍历,当队列为空时为循环终止条件,计算矩阵中2的个数

#include<iostream>
#define MAXQ 100					
#define MAxN 100						
using namespace std;

int Maze[MAxN][MAxN] =
{ 
  {0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,1,0,1,0,1,0,1,0,1,0,0,1,0,0,1,1,0,0,0,0,0,0,1,1},
  {0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,1,1},
  {0,1,0,1,1,1,1,0,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,0,1,0,1,0,0,1,1,1,1,1},
  {1,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,1,0,0},
  {0,1,1,0,1,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,1},
  {0,0,0,0,0,1,1,0,1,0,1,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1,0,1,1,0,0,0,1,0,1,0,1,0,0,1},
  {0,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,1,1},
  {0,1,0,0,1,1,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,0,1,1,1,0,0,0,0},
  {0,0,1,0,0,1,1,0,1,0,1,0,0,1,1,0,0,0,1,1,1,1,0,0,1,1,0,1,1,0,0,1,1,0,1,0,0,0,1,0},
  {1,1,1,1,0,0,0,1,1,1,1,0,1,0,0,0,0,0,1,1,1,0,0,1,0,0,0,1,0,0,1,0,1,1,1,0,1,1,0,1},
  {0,0,1,1,1,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1,0,0,1,1,0,1,1,1,0,1,0,0,0,0,1},
  {0,0,0,0,0,0,0,1,0,1,0,1,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,1,0,0,1,1,0,1,1,1,0,0},
  {0,0,0,0,1,0,0,0,0,0,0,1,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,0,1,1,1,0,1,1,1,0,1,1,0,0},
  {0,0,1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,0,1,0,0,0,1,1,0,0,0,0,1,0,1,0,0,0,1,1},
  {0,1,1,0,1,1,0,0,0,0,1,0,0,0,1,1,0,1,1,0,1,0,0,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,1,1},
  {0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
  {0,0,1,1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,1,0,0,1,1,0,0,1,0,1,0,0,0,1,1,0},
  {1,1,1,0,1,0,1,0,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,1,1,0,0,1,0,0,1,1,0,0,1,1,1,0,1,0},
  {0,0,0,0,1,0,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,1,0,0,1,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1},
  {0,1,0,0,0,1,0,0,1,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,1,0,1,0,1},
  {1,0,0,0,0,0,1,0,0,1,1,0,0,0,1,0,0,1,1,0,0,1,1,1,1,1,0,1,0,1,1,0,0,1,1,1,0,0,0,1},
  {0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,1,0,0,0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,0,1,0,1,1,0,1},
  {0,0,1,0,1,1,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1,1,0,0,1,1},
  {0,0,0,0,0,1,1,1,0,1,0,0,1,0,0,1,0,0,0,1,1,1,0,1,1,0,0,0,1,0,0,1,1,1,0,1,0,1,0,0},
  {0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,1,0,0,0,1,0,1,0,1,1,0,0,0,0,0,0,0,1,0,1,0,1},
  {1,0,0,1,1,1,1,0,1,0,0,1,0,1,1,0,0,1,1,0,1,0,1,0,1,1,1,0,0,0,0,0,0,0,1,0,1,1,1,0},
  {0,1,1,0,0,1,1,1,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1,0,0,1,0,0,1,1,0,0,0,1,0},
  {1,1,0,1,0,0,0,0,0,0,0,1,0,0,1,0,0,1,1,0,0,1,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0,1,0,1},
  {1,0,0,1,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0},
  {1,0,0,1,0,1,1,0,1,0,1,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,0,1,1,0,0,0,0},
  {0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,1,0,1,1,1,1,0,1,0,0,0,1,0,1,1,1,0,1,1,0,0,0,1}
};


int H[4] = { 0, 1, 0, -1 };			//水平偏移量,下标对应方位号0~3
int V[4] = { -1, 0, 1, 0 };			//垂直偏移量

struct Position						//队列元素类型
{
	int x, y;						//当前方块位置
};

Position qu[MAXQ];					//定义一个队列qu
int front = -1, rear = -1;			//定义队头和队尾


//输出迷宫并输出迷宫中的2的个数
void print_2()
{
	int count = 0;
	for (int i = 0; i < 30; i++)
	{
		for (int j = 0; j < 40; j++)
		{
			cout << Maze[i][j];
			if (Maze[i][j] == 2)
			{
				count++;
			}
		}
		cout << endl;
	}
	cout << "迷宫中2的个数为:" << count << endl;
}


void BFS(int x, int y)			 //求从(x,y)出发的一条迷宫路径
{
	Position p, p1, p2;
	p.x = x; p.y = y; 		//建立入口结点
	Maze[p.x][p.y] = 2;				//改为2避免重复查找
	rear++; qu[rear] = p;				//入口方块进队
	while (front != rear)				//队不空循环
	{
		front++; p1 = qu[front];		//出队方块p1;
		for (int k = 0; k < 4; k++)		//试探p1的每个相邻方位
		{
			p2.x = p1.x + V[k];			//找到p1的相邻方块p2
			p2.y = p1.y + H[k];
			if (p2.x >= 0 && p2.y >= 0 && p2.x < 30 && p2.y < 40 && Maze[p2.x][p2.y] == 0)
			{	//方块p2有效并且可走
				Maze[p2.x][p2.y] = 2;	//改为2避免重复查找
				rear++;	qu[rear] = p2;	//方块p2进队
			}
		}
	}
	print_2();
}

int main()
{
	int x = 0, y = 0;		
	BFS(x, y);
	return 0;
}

输出结果:

image-20231206204319567

案例3:跳动方格

如果两个相邻方格(上下左右四个方向相邻)内的数的最大公约数大于 1 ,则可以从其中一个方格移动到另一个方格,当然也可以从另一个方格移回第一个方格。
假设开始时站在第 r 行第 c 列,请问可以移动到方格图内的多少个方格?

样例输入

3 4
3 6 5 5
2 4 3 5
7 8 3 8
3 2

样例输出

5

思路:采用广度优先遍历,在将可行的方格放入到队列之前,计数+1

易错点+技巧点:

二维数组的初始化;

用同样尺寸的数组记录某个位置是否已经被访问过了;

没有记录路线的要求就可以不使用pre属性

二维数组的动态初始化:

//动态定义一个二维数组,并将其全部值初始化为0
	int** map;
	map = new int* [m];
	
	for (int i = 0; i < m; i++)
	{
        int* row = new int[n];
		for (int j = 0; j < n; j++)
		{
			row[j] = 1;
		}
		map[i] = row;
	}

或者

//动态定义一个二维数组,并将其全部值初始化为0
	int** map;
	map = new int* [m];

	for (int i = 0; i < m; i++)
	{
		map[i] = new int[n];
		for (int j = 0; j < n; j++)
		{
			map[i][j] = 1;
		}
	}

使用memset

int** map = new int* [m];
for (int i = 0; i < m; ++i) {
	map[i] = new int[n];
	memset(map[i], 3, n * sizeof(int));
}
#include<iostream>
#define MAXQ 100					//队列大小
#define MAxN 100						//最大迷宫大小
using namespace std;

int** map;
int** flag;	//记录是否已经被访问了
int m;
int n;

int H[4] = { 0, 1, 0, -1 };			//水平偏移量,下标对应方位号0~3
int V[4] = { -1, 0, 1, 0 };			//垂直偏移量

struct Position						//队列元素类型
{
	int x, y;//当前方块位置
};

Position qu[MAXQ];					//定义一个队列qu
int front = -1, rear = -1;			//定义队头和队尾

bool moreThanOne(int x, int y)
{
	//两个数的最大公约数大于1,返回true
	int theSmallOne = x < y ? x : y;
	for (int i = 2; i <= theSmallOne; i++)
	{
		if (x % i == 0 && y % i == 0) return true;
	}
	return false;
}

void printMap()
{
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			cout << map[i][j]<<"  ";
		}
		cout << endl;
	}
}

void BFS(int x, int y)			 
{
	int count = 0;          //记录可以跳跃的个数
	Position p, p1, p2;
	p.x = x; p.y = y; 		//建立入口结点			
	rear++; qu[rear] = p;		
	count++;

	while (front != rear)				//队不空循环
	{
		front++; p1 = qu[front];		//出队方块p1;
		for (int k = 0; k < 4; k++)		//试探p1的每个相邻方位
		{
			p2.x = p1.x + V[k];			//找到p1的相邻方块p2
			p2.y = p1.y + H[k];

			if (p2.x >= 0 && p2.y >= 0 && p2.x < m && p2.y < n && moreThanOne(map[p2.x][p2.y], map[p1.x][p1.y]) && flag[p2.x][p2.y] != -1)
			{
				//方块p2有效并且可走
				flag[p1.x][p1.y] = -1;	//改为-1避免重复查找
				rear++;	qu[rear] = p2;	//方块p2进队
				count++;
			}
		}
	}

	printMap();
	cout << count << endl;
}

int main()
{
	//初始化矩阵
	cin >> m >> n;
	map = new int* [m];       //动态初始化二维数组这一步很重要,但是这一步最容易忘
	flag = new int* [m];
	for (int i = 0; i < m; i++)
	{
		int* row = new int[n];
		int* flag_row = new int[n];
		for (int j = 0; j < n; j++)
		{
			cin >> row[j];
			flag_row[j] = 0;
		}
		map[i] = row;
		flag[i] = flag_row;
	}

	//输入初始化的位置
	int x, y;
	cin >> x >> y;
	BFS(x - 1, y - 1);

	return 0;
}

运行结果:

image-20231207151238260

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 广度优先可以用来寻找迷宫的路径。下面是一个示例代码: ```python from queue import Queue def bfs(maze, start, end): queue = Queue() visited = set() queue.put(start) visited.add(start) path = {} path[start] = None while not queue.empty(): current = queue.get() if current == end: break for neighbor in get_neighbors(maze, current): if neighbor not in visited: queue.put(neighbor) visited.add(neighbor) path[neighbor] = current return get_path(path, start, end) def get_neighbors(maze, cell): neighbors = [] rows, cols = len(maze), len(maze[0]) row, col = cell if row > 0 and maze[row-1][col] == 0: neighbors.append((row-1, col)) if row < rows-1 and maze[row+1][col] == 0: neighbors.append((row+1, col)) if col > 0 and maze[row][col-1] == 0: neighbors.append((row, col-1)) if col < cols-1 and maze[row][col+1] == 0: neighbors.append((row, col+1)) return neighbors def get_path(path, start, end): current = end result = [] while current != start: result.append(current) current = path[current] result.append(start) result.reverse() return result ``` 其,`maze` 是一个二维数组,表示迷宫,其 0 表示可通过的格子,1 表示障碍物。`start` 和 `end` 分别表示起点和终点。 函数首先创建一个队列 `queue` 和一个集合 `visited`,将起点加入队列和集合。然后创建一个字典 `path`,用于记录每个格子的前驱格子。接着进入循环,直到队列为空或者找到终点为止。每次从队列取出一个格子 `current`,并遍它的相邻格子,如果相邻格子是可通过的且没有被访问过,则将其加入队列和集合,并在 `path` 记录它的前驱格子为 `current`。 最后,调用 `get_path` 函数获取从起点到终点的路径。`get_path` 函数从终点开始,沿着 `path` 记录的前驱格子一路向前,直到到达起点为止。 ### 回答2: 广度优先是一种的遍算法,用于寻找迷宫的路径。在迷宫,我们可以将每个格子看作一个节点,相邻的格子之间存在着边。迷宫通畅的路径就是从起始节点到目标节点的一条有效路径。 广度优先算法的基本思想是从起始节点开始,依次访问其所有相邻的节点,并将这些节点依次加入一个队列。然后再从队列头取出下一个节点进行访问,直到找到目标节点或者队列为空。 具体实现迷宫路径的广度优先可以按照以下步骤进行: 1. 创建一个队列,将起始节点加入队列。 2. 创建一个visited集合,用于记录已经访问过的节点。 3. 创建一个prev字典,用于记录每个节点的前驱节点,即每个节点是从哪个节点遍而来。 4. 对于队列不为空的情况,重复以下步骤: - 从队列头取出一个节点作为当前节点。 - 如果当前节点是目标节点,则停止遍,此时可以通过prev字典生成迷宫的路径。 - 否则,将当前节点标记为已访问,并遍其所有相邻的节点: - 如果相邻节点没有被访问过,则将其加入队列,并将当前节点设置为其前驱节点。 5. 如果队列为空,但仍未找到目标节点,则说明迷宫不存在可到达目标节点的路径。 通过广度优先,我们可以找到从起始节点到目标节点的最短路径,因为遍过程,第一次访问到目标节点时,一定是经过的路径最短的。使用prev字典即可回溯出路径。 ### 回答3: 广度优先是一种的搜索算法,可以用于解决迷宫路径的问题迷宫可以看作是一个由行和列组成的二维矩阵,其的每个元素可以是墙壁、通路或终点。 广度优先利用队列来实现,从起点开始,将起点入队,并将其标记为已访问。然后,不断从队列取出元素,并检查其周围的相邻节点是否可访问。如果可访问,则将其入队,并标记为已访问。直到队列为空为止,即找到了终点或者无法找到路径。 具体实现时,可以使用一个二维数组来表示迷宫,其的元素值表示该位置的状态(0表示墙壁,1表示通路,2表示终点)。另外,可以使用一个二维数组来记录每个位置的步数,初始值为0。 首先,将起点入队,并将起点的步数设置为1。然后,进入循环,直到队列为空。在循环,从队列取出一个节点,再枚举其四个相邻节点(上、下、左、右)。对于每个相邻节点,如果该节点可访问且未被访问过,则将其入队,并将其步数设置为当前节点的步数加1。同时,标记该位置为已访问。如果某个相邻节点的值为2,则说明找到了终点,此时可退出循环。 最后,可以通过回溯的方式,从终点开始,沿着步数递减的路径依次找到起点,并将路径上的位置存入一个栈。最后,将栈的元素依次出栈,即可得到起点到终点的路径。 总结起来,广度优先实现迷宫路径的步骤如下: 1. 将起点入队,并将其标记为已访问。 2. 进入循环,直到队列为空。 - 从队列取出一个节点。 - 枚举其四个相邻节点。 - 如果相邻节点可访问且未被访问过,将其入队,并将其步数设置为当前节点的步数加1。同时,标记该位置为已访问。 - 如果某个相邻节点的值为2,退出循环。 3. 通过回溯的方式,找到起点到终点的路径,并存入一个栈。 4. 将栈的元素依次出栈,得到起点到终点的路径。 通过以上步骤,就可以使用广度优先找到迷宫的路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值