迷宫问题(maze problem)—— 深度优先与广度优先搜索求解

article/2025/9/17 3:58:06

文章目录

  • 1.问题简介
  • 2.求解方法
  • 3.深度优先搜索加回溯法
    • 3.1 求解第一条可行路径
    • 3.2 求解最短路径
  • 4.广度优先搜索
  • 小结
  • 参考文献

1.问题简介

给定一个迷宫,指明起点和终点,找出从起点出发到终点的有效可行路径,就是迷宫问题(maze problem)。

迷宫可以以二维数组来存储表示。0表示通路,1表示障碍。注意这里规定移动可以从上、下、左、右四方方向移动。坐标以行和列表示,均从0开始,给定起点(0,0)和终点(4,4),迷宫表示如下:

int maze[5][5]={{0,0,0,0,0},{0,1,0,1,0},{0,1,1,0,0},{0,1,1,0,1},{0,0,0,0,0}
};

那么下面的迷宫就有两条可行的路径,分别为:

(0,0) (0,1) (0,2) (0,3) (0,4) (1,4) (2,4) (2,3) (3,3) (4,3) (4,4)(0,0) (1,0) (2,0) (3,0) (4,0) (4,1) (4,2) (4,3) (4,4) ;

可见,迷宫可行路径可能有多条,且路径长度可能不一。

2.求解方法

迷宫问题的求解可以抽象为连通图的遍历,因此主要有两种方法。

第一种方法:深度优先搜索(DFS)加回溯。
优点: 无需像广度优先搜索那样(BFS)记录前驱结点。
缺点: 找到的第一条可行路径不一定是最短路径,如果需要找到最短路径,那么需要找出所有可行路径后,再逐一比较,求出最短路径。

第二种方法:广度优先搜索(BFS)。
优点: 找出的第一条路径就是最短路径。
缺点: 需要记录结点的前驱结点,来形成路径。

下面将给出上面两种方法的具体步骤和实现。

3.深度优先搜索加回溯法

3.1 求解第一条可行路径

实现步骤:
(1)给定起点和终点,判断二者的合法性,如果不合法,返回;
(2)如果起点和终点合法,将起点入栈;
(3)取栈顶元素,求其邻接的未被访问的无障碍结点。求如果有,记其为已访问,并入栈。如果没有则回溯上一结点,具体做法是将当前栈顶元素出栈。其中,求邻接无障碍结点的顺序可任意,本文实现是以上、右、下、左的顺序求解。
(4)重复步骤(3),直到栈空(没有找到可行路径)或者栈顶元素等于终点(找到第一条可行路径)。

实现示例:

#include <iostream>
#include <stack>
using namespace std;struct Point{  //行与列int row;  int col;  Point(int x,int y){this->row=x;this->col=y;}bool operator!=(const Point& rhs){if(this->row!=rhs.row||this->col!=rhs.col)return true;return false;}
};  //func:获取相邻未被访问的节点
//para:mark:结点标记,point:结点,m:行,n:列
//ret:邻接未被访问的结点
Point getAdjacentNotVisitedNode(bool** mark,Point point,int m,int n){Point resP(-1,-1);if(point.row-1>=0&&mark[point.row-1][point.col]==false){//上节点满足条件resP.row=point.row-1;resP.col=point.col;return resP;}if(point.col+1<n&&mark[point.row][point.col+1]==false){//右节点满足条件resP.row=point.row;resP.col=point.col+1;return resP;}if(point.row+1<m&&mark[point.row+1][point.col]==false){//下节点满足条件resP.row=point.row+1;resP.col=point.col;return resP;}if(point.col-1>=0&&mark[point.row][point.col-1]==false){//左节点满足条件resP.row=point.row;resP.col=point.col-1;return resP;}return resP;
}//func:给定二维迷宫,求可行路径
//para:maze:迷宫;m:行;n:列;startP:开始结点 endP:结束结点; pointStack:栈,存放路径结点
//ret:无
void mazePath(void* maze,int m,int n,const Point& startP,Point endP,stack<Point>& pointStack){//将给定的任意列数的二维数组还原为指针数组,以支持下标操作int** maze2d=new int*[m];for(int i=0;i<m;++i){maze2d[i]=(int*)maze + i*n;}//起点和终点必须为无障碍结点,否则输入错误if(maze2d[startP.row][startP.col] == 1 || maze2d[endP.row][endP.col] == 1){return ;}//建立各个节点访问标记bool** mark=new bool*[m];for(int i=0;i<m;++i) {mark[i]=new bool[n];}for(int i=0;i<m;++i) {for(int j=0;j<n;++j){mark[i][j]=*((int*)maze+i*n+j);}}//将起点入栈pointStack.push(startP);mark[startP.row][startP.col]=true;//栈不空并且栈顶元素不为结束节点while(pointStack.empty()==false&&pointStack.top()!=endP){Point adjacentNotVisitedNode=getAdjacentNotVisitedNode(mark,pointStack.top(),m,n);if(adjacentNotVisitedNode.row==-1){ //没有未被访问的相邻节点pointStack.pop(); //回溯到上一个节点continue;}//入栈并设置访问标志为truemark[adjacentNotVisitedNode.row][adjacentNotVisitedNode.col]=true;pointStack.push(adjacentNotVisitedNode);}
}int main() {int maze[5][5]={{0,0,0,0,0},{0,1,0,1,0},{0,1,1,0,0},{0,1,1,0,1},{0,0,0,0,0}};Point startP(0,0);Point endP(4,4);stack<Point>  pointStack;mazePath(maze,5,5,startP,endP,pointStack);//没有找打可行解if(pointStack.empty()==true) {cout<<"no right path"<<endl;} else {stack<Point> tmpStack;cout<<"path:";while(pointStack.empty()==false) {tmpStack.push(pointStack.top());pointStack.pop();}while (tmpStack.empty()==false) {printf("(%d,%d) ",tmpStack.top().row,tmpStack.top().col);tmpStack.pop();}}
}

程序输出:

path:(0,0) (0,1) (0,2) (0,3) (0,4) (1,4) (2,4) (2,3) (3,3) (4,3) (4,4)

可见该条路径不是最短路径。因为程序中给定的迷宫还有一条更短路径为:

(0,0) (1,0) (2,0) (3,0) (4,0) (4,1) (4,2) (4,3) (4,4)

如果我们调整调用寻找下一个未访问的相邻结点的顺序,可换成先左右,后上下,可能会得到更短的路径,但无法确保在任何情况下都能得到最短路径。

3.2 求解最短路径

根据上面的方法我们可以在此基础之上进行改进,求出迷宫的最短路径。具体做法如下:
(1)让已经访问过的结点可以再次被访问,具体做法是将mark标记改为当前结点到起点的距离,作为当前结点的权值。即从起点开始出发,向四个方向查找,每走一步,把走过的点的值+1;

(2)寻找栈顶元素的下一个可访问的相邻结点,条件就是栈顶元素的权值加1必须小于下一个节点的权值(墙不能走,未被访问的结点权值为0);

(3)如果访问到终点,记录当前最短的路径。如果不是,则继续寻找下一个结点;

(4)重复步骤(2)和(3)直到栈空(迷宫中所有符合条件的结点均被访问)。

实现示例:

#include <iostream>
#include <stack>
#include <vector>
using namespace std;struct Point {  //行与列int row;  int col;  Point(int x,int y){this->row=x;this->col=y;}bool operator!=(const Point& rhs) {if(this->row!=rhs.row||this->col!=rhs.col)return true;return false;}bool operator==(const Point& rhs) const {if(this->row==rhs.row&&this->col==rhs.col)return true;return false;}
};  //func:获取相邻未被访问的节点
//para:mark:结点标记;point:结点;m:行;n:列;endP:终点
//ret:邻接未被访问的结点
Point getAdjacentNotVisitedNode(int** mark, Point point, int m, int n, Point endP) {Point resP(-1,-1);if(point.row-1 >= 0) {//上结点满足条件if(mark[point.row-1][point.col] == 0 || mark[point.row][point.col]+1<mark[point.row-1][point.col]) {resP.row=point.row-1;resP.col=point.col;return resP;}}if(point.col+1 < n) {//右结点满足条件if(mark[point.row][point.col+1] == 0 || mark[point.row][point.col]+1<mark[point.row][point.col+1]){resP.row=point.row;resP.col=point.col+1;return resP;}}if(point.row+1 < m){//下结点满足条件if(mark[point.row+1][point.col] == 0 || mark[point.row][point.col]+1<mark[point.row+1][point.col]){resP.row=point.row+1;resP.col=point.col;return resP;}}if(point.col-1 >= 0){//左结点满足条件if(mark[point.row][point.col-1] == 0 || mark[point.row][point.col]+1<mark[point.row][point.col-1]){resP.row=point.row;resP.col=point.col-1;return resP;}}return resP;
}//func:给定二维迷宫,求可行路径
//para:maze:迷宫;m:行;n:列;startP:开始结点 endP:结束结点; pointStack:栈,存放路径结点;vecPath:存放最短路径
//ret:无
void mazePath(void* maze,int m,int n, Point& startP, Point endP,stack<Point>& pointStack,vector<Point>& vecPath)
{//将给定的任意列数的二维数组还原为指针数组,以支持下标操作int** maze2d=new int*[m];for(int i=0;i<m;++i){maze2d[i]=(int*)maze+i*n;}//输入错误if(maze2d[startP.row][startP.col] == -1 || maze2d[endP.row][endP.col] == -1){return;}//建立各个节点访问标记,表示结点到到起点的权值,也记录了起点到当前结点路径的长度int** mark=new int*[m];for(int i=0;i<m;++i){mark[i]=new int[n];}for(int i=0;i<m;++i){for(int j=0;j<n;++j){mark[i][j] = *((int*)maze+i*n+j);}}//起点等于终点if(startP==endP){vecPath.push_back(startP);return;}//增加一个终点的已被访问的前驱结点集vector<Point> visitedEndPointPreNodeVec;//将起点入栈pointStack.push(startP);mark[startP.row][startP.col] = 1;//栈不空并且栈顶元素不为结束节点while(pointStack.empty() == false){Point adjacentNotVisitedNode=getAdjacentNotVisitedNode(mark,pointStack.top(),m,n,endP);//没有符合条件的相邻结点if(adjacentNotVisitedNode.row == -1){//回溯到上一个节点,继续深度搜索pointStack.pop();continue;}//以较短的路径,找到了终点if(adjacentNotVisitedNode == endP){mark[adjacentNotVisitedNode.row][adjacentNotVisitedNode.col] = mark[pointStack.top().row][pointStack.top().col] + 1;pointStack.push(endP);stack<Point> pointStackTemp=pointStack;vecPath.clear();while (pointStackTemp.empty() == false){//vecPath 存放的是逆序路径vecPath.push_back(pointStackTemp.top());pointStackTemp.pop();}pointStack.pop(); //将终点出栈continue;}//入栈并设置结点权值mark[adjacentNotVisitedNode.row][adjacentNotVisitedNode.col]=mark[pointStack.top().row][pointStack.top().col]+1;pointStack.push(adjacentNotVisitedNode);}
}int maze[5][5]={{0, 0, 0, 0,0},{0,-1, 0,-1,0},{0,-1,-1, 0,0},{0,-1,-1, 0,-1},{0, 0, 0, 0, 0}
};int main(){Point startP(0,0);Point endP(4,4);stack<Point>  pointStack;vector<Point> vecPath;mazePath(maze,5,5,startP,endP,pointStack,vecPath);if(vecPath.empty()==true){cout << "no right path" << endl;} else {cout<<"shortest path:";for(auto i=vecPath.rbegin();i!=vecPath.rend();++i){printf("(%d,%d) ",i->row,i->col);}}
}

程序输出最短路径如下:
这里写图片描述
如果将程序的迷宫改为如下:

int maze[5][3] = {
0, -1, 0, 
0, -1 ,0,
1, -1, 0 ,
0, -1, 0,
0 ,0,  0};

输出:
这里写图片描述
根据改进的办法,可以看到上段代码修改的地方主要有三个地方:
(1)mark 标记改为结点权值,记录起点到结点的路径长度。特殊地,起点的权值记为 1。
(2)为适应 mark 标记,将迷宫的墙改为 -1,以免与结点权值混淆。
(3)求解下一个访问的结点,判断条件是结点的权值要小于其当前权值。

4.广度优先搜索

广度优先搜索的优点是找出的第一条路径就是最短路径,所以经常用来搜索最短路径,思路和图的广度优先遍历一样,需要借助队列。

实现步骤:
(1)从入口元素开始,判断它上下左右的邻边元素是否满足条件,如果满足条件就入队列;
(2)取队首元素并出队列。寻找其相邻未被访问的元素,将其如队列并标记元素的前驱节点为队首元素;
(3)重复步骤(2),直到队列为空(没有找到可行路径)或者找到了终点。最后从终点开始,根据节点的前驱节点找出一条最短的可行路径。

实现示例:

#include <iostream>
#include <queue>
using namespace std;struct Point {//行与列int row;  int col;  //默认构造函数Point(){row=col=-1;}Point(int x,int y){this->row=x;this->col=y;}bool operator==(const Point& rhs) const{if(this->row==rhs.row&&this->col==rhs.col)return true;return false;}
};int maze[5][5] = {{0,0,0,0,0},{0,1,0,1,0},{0,1,1,1,0},{0,1,0,0,1},{0,0,0,0,0}
};void mazePath(void* maze,int m,int n, Point& startP, Point endP,vector<Point>& shortestPath){int** maze2d=new int*[m];for(int i=0;i<m;++i){maze2d[i]=(int*)maze+i*n;}if(maze2d[startP.row][startP.col]==1||maze2d[startP.row][startP.col]==1) return ; //输入错误if(startP==endP){ //起点即终点shortestPath.push_back(startP);return;}//mark标记每一个节点的前驱节点,如果没有则为(-1,-1),如果有,则表示已经被访问Point** mark=new Point*[m];for(int i=0;i<m;++i){mark[i]=new Point[n];}queue<Point> queuePoint;queuePoint.push(startP);//将起点的前驱节点设置为自己mark[startP.row][startP.col]=startP;while(queuePoint.empty()==false){Point pointFront=queuePoint.front();queuePoint.pop();if(pointFront.row-1>=0 && maze2d[pointFront.row-1][pointFront.col]==0){//上节点连通if(mark[pointFront.row-1][pointFront.col]==Point()){//上节点未被访问,满足条件,如队列mark[pointFront.row-1][pointFront.col]=pointFront;queuePoint.push(Point(pointFront.row-1,pointFront.col)); //入栈if(Point(pointFront.row-1,pointFront.col)==endP){ //找到终点break;}}}if(pointFront.col+1<n && maze2d[pointFront.row][pointFront.col+1]==0){//右节点连通if(mark[pointFront.row][pointFront.col+1]==Point()){//右节点未被访问,满足条件,如队列mark[pointFront.row][pointFront.col+1]=pointFront;queuePoint.push(Point(pointFront.row,pointFront.col+1));	//入栈if(Point(pointFront.row,pointFront.col+1)==endP){ //找到终点break;}}}if(pointFront.row+1<m && maze2d[pointFront.row+1][pointFront.col]==0){//下节点连通if(mark[pointFront.row+1][pointFront.col]==Point()){//下节点未被访问,满足条件,如队列mark[pointFront.row+1][pointFront.col]=pointFront;queuePoint.push(Point(pointFront.row+1,pointFront.col));	//入栈if(Point(pointFront.row+1,pointFront.col)==endP){ //找到终点break;}}}if(pointFront.col-1>=0 && maze2d[pointFront.row][pointFront.col-1]==0){//左节点连通if(mark[pointFront.row][pointFront.col-1]==Point()){//上节点未被访问,满足条件,如队列mark[pointFront.row][pointFront.col-1]=pointFront;queuePoint.push(Point(pointFront.row,pointFront.col-1));	//入栈if(Point(pointFront.row,pointFront.col-1)==endP){ //找到终点break;}}}}if(queuePoint.empty()==false){int row=endP.row;int col=endP.col;shortestPath.push_back(endP);while(!(mark[row][col]==startP)){shortestPath.push_back(mark[row][col]);row=mark[row][col].row;col=mark[row][col].col;}shortestPath.push_back(startP);}
}int main() {Point startP(0,0);Point endP(4,4);vector<Point> vecPath;mazePath(maze,5,5,startP,endP,vecPath);if(vecPath.empty()==true)cout<<"no right path"<<endl;else{cout<<"shortest path:";for(auto i=vecPath.rbegin();i!=vecPath.rend();++i)printf("(%d,%d) ",i->row,i->col);}getchar();
}

程序输出:
这里写图片描述
代码的几点说明:
(1)BFS 求迷宫最短路径,记录每个节点的前驱节点使用了mark标记。可见,三种方法中mark标记可以根据实际需求灵活为其赋予意义;
(2)特殊的,起始节点的前驱设置为其本身。

小结

告诫。看着别人的代码去理解问题是如何求解的,对于求解算法题来说,这种方法是错误的。正确的方法是看别人的求解思路,理解如何求解后,给出自己的实现,才能够真正深刻地掌握算法题的求解。经过自己思考的才能真正成为自己的东西。不然的话,看着别人的代码痛苦不说,而且每个人的实现在很多细节上不尽相同,即使花了很长时间,暂时弄明白了,我想过不了多久就会忘记。这样,得不偿失!


参考文献

[1] 迷宫最短路径问题解析
[2] 迷宫问题


http://chatgpt.dhexx.cn/article/5G8MgXfb.shtml

相关文章

图深度优先、广度优先遍历(java)

一、图的遍历 图的遍历&#xff0c;即是对结点的访问。一个图有那么多个结点&#xff0c;如何遍历这些结点,需要特定策略&#xff0c;一般有两种访问策略:(1)深度优先遍历(2)广度优先遍历深度优先遍历基本思想。 二、深度优先遍历 图的深度优先搜索(Depth First Search)。 深…

总结深度优先与广度优先的区别

3 总结深度优先与广度优先的区别 1、区别 1&#xff09; 二叉树的深度优先遍历的非递归的通用做法是采用栈&#xff0c;广度优先遍历的非递归的通用做法是采用队列。 2&#xff09; 深度优先遍历&#xff1a;对每一个可能的分支路径深入到不能再深入为止&#xff0c;而且每个结…

连通图里的深度优先和广度优先遍历

从图中的某个顶点出发&#xff0c;按照某种搜索方法沿着图的边访问图中的所有顶点&#xff0c;使得每个顶点仅被访问一次&#xff0c;这个过程称为图的遍历。图的遍历有两种&#xff1a;深度优先遍历和广度优先遍历。   图分为连通图和非连通图&#xff0c;这里主要讨论连通图…

广度优先算法

广度优先算法 本文主要以介绍算法思想为主这里并没有进行源码实现&#xff0c;但是给出推荐使用的数据结构和主要思想。 首先介绍一下广度优先算法&#xff0c;假设要查找AB两点之间的最短距离&#xff0c;以A为起点B为终点。可以先遍历A的相邻节点&#xff0c;这些节点称之为…

C语言基于邻接表的图的深度优先、广度优先遍历

目录 1 深度优先&#xff08;Depth_First Search&#xff09; 2 广度优先&#xff08;Broadth_First Search&#xff09; 3 基于邻接表的深度优先、广度优先遍历 4 源代码示例 4.1 深度优先 4.2广度优先 假设有无向图G &#xff08;V&#xff0c;E&#xff09;&#xff…

数据结构之深度优先和广度优先遍历

文章目录 图为什么要有图图的常用概念邻接矩阵邻接表图的深度优先遍历深度优先遍历基本思想深度优先遍历算法步骤深度优先算法的代码实现 图的广度优先遍历广度优先遍历基本思想广度优先遍历算法步骤广度优先算法的代码实现图结构完整代码 图 为什么要有图 1)前面我们学了线性…

图的深度优先和广度优先遍历算法

编写一个程序&#xff0c;输出下面带权有向图的邻接表&#xff0c;并根据该邻接表&#xff0c;实现图的遍历运算&#xff0c;具体要求如下&#xff1a; (1)从顶点0开始的深度优先遍历序列(递归算法) (2)从顶点0开始的深度优先遍历序列(非递归算法) (3)从顶点0开始的广度优先遍历…

算法之深度优先、广度优先算法

目录 前言&#xff1a; 搜索算法&#xff1a; 广度优先搜索算法 深度优先搜索算法 问题&#xff1a;如何找出社交网络中某个用户的三度好友关系&#xff1f; 总结&#xff1a; 参考资料&#xff1a; 前言&#xff1a; 图这种数据结构经常用于表示一个社交网络&#x…

广度优先搜索与深度优先搜索

广度优先搜索&#xff08;宽度优先搜索&#xff0c;BFS&#xff09;和深度优先搜索&#xff08;DFS&#xff09;算法的应用非常广泛&#xff0c;本篇文章主要介绍BFS与DFS的原理、实现和应用。 深度优先搜索 图的深度优先搜索(Depth First Search)&#xff0c;和树的先序遍历…

深度优先遍历与广度优先遍历

1、深度优先遍历(Depth First Search, 简称 DFS) 1.1、主要思路 从图中一个未访问的顶点 V 开始&#xff0c;沿着一条路一直走到底&#xff0c;然后从这条路尽头的节点回退到上一个节点&#xff0c;再从另一条路开始走到底…&#xff0c;不断递归重复此过程&#xff0c;直到所…

深度优先与广度优先

深度优先遍历简称DFS&#xff08;Depth First Search&#xff09;&#xff0c;广度优先遍历简称BFS&#xff08;Breadth First Search&#xff09;&#xff0c;它们是遍历图当中所有顶点的两种方式。 深度优先遍历&#xff1a; 选取一个节点开始&#xff0c;沿着一条路一直走…

深度优先遍历(DFS)和广度优先遍历(BFS)

深度优先遍历&#xff08;DFS&#xff09;和广度优先遍历&#xff08;BFS&#xff09; 图的遍历&#xff1a;所谓遍历&#xff0c;即是对结点的访问。一个图有多个结点&#xff0c;如何遍历这些结点&#xff0c;有两种访问策略&#xff1a; 深度优先遍历(Depth First Search, …

深度优先与广度优先的区别!

从深度优先和广度优先两个角度解决同一个问题 题目 从一号顶点开始遍历这个图&#xff0c;使用深度优先搜索和广度优先搜索的2种遍历结果 深度优先遍历的主要思想就是&#xff0c;首先以一个未被访问过的顶点作为起始顶点&#xff0c;沿着当前顶点的边走到未访问过的顶点&…

数据结构:图的遍历--深度优先、广度优先

图的遍历&#xff1a;深度优先、广度优先 遍历 图的遍历是指从图中的某一顶点出发&#xff0c;按照一定的策略访问图中的每一个顶点。当然&#xff0c;每个顶点有且只能被访问一次。 在图的遍历中&#xff0c;深度优先和广度优先是最常使用的两种遍历方式。这两种遍历方式对无…

深度优先搜索与广度优先搜索

算法是作用于具体数据结构之上的&#xff0c;深度优先搜索算法和广度优先搜索算法都是基于“图”这种数据结构的。这是因为&#xff0c;图这种数据结构的表达能力很强&#xff0c;大部分涉及搜索的场景都可以抽象成“图”。 图上的搜索算法&#xff0c;最直接的理解就是&#…

广度优先搜索和深度优先搜索

文章目录 1. 前言2. 广度优先搜索和深度优先搜索1&#xff09;深度优先搜索2&#xff09;广度优先搜索 3. 深度优先搜索算法框架1&#xff09;二叉树深度优先搜索模板2&#xff09;图深度优先搜索模板3&#xff09;二维矩阵深度优先搜索模板 4. 广度优先搜索算法框架1&#xff…

深度优先和广度优先算法

1、深度优先算法 遍历规则&#xff1a;不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。 最后得出的结果为&#xff1a;ABDECFHG。 Python代码实现的伪代码如下&#xff1a; 2、广度优先算法&#xff1a; 遍历规则&#xff1a; 1&#xff09;先访问完当…

深度优先搜索(DFS)和广度优先搜索(BFS)

代码随想录 深度优先搜索和广度优先搜索&#xff0c;都是图形搜索算法&#xff0c;它两相似&#xff0c;又却不同&#xff0c;在应用上也被用到不同的地方。这里拿一起讨论&#xff0c;方便比较。 先给大家说一下两者大概的区别&#xff1a; 如果搜索是以接近起始状态的程序依次…

算法:深度优先和广度优先(DFS,BFS)

一丶深度优先&#xff08;DFS&#xff09; 深度优先顾名思义: 就是往深的地方优先查找或遍历。 如图二叉树&#xff0c;想遍历树中所有结点可以用中序遍历&#xff0c;前序或后序。如果某一结点还有子结点就会往深处就是往下一结点&#xff0c;一直遍历直到最后一个结点没有子…

【算法】深度优先和广度优先

本文只是总结的相关概念&#xff0c;仅供自己复习&#xff0c;严禁转载&#xff0c;文末附有本文内容涉及的文章链接&#xff0c;请点开链接查看原文&#xff01; &#xff08;一&#xff09;深度优先 深度优先搜索属于图算法的一种&#xff0c;是一个针对图和树的遍历算法&am…