深度优先和广度优先算法
1 深度优先搜索算法(DFS)
1.1 DFS解决问题
解决连通性的问题,即给定两个点,一个起始点(或起始状态),一个终点(或最终状态),判断是否有一条路径能从起点连接到终点。
很多情况下,连通的路径有很多条,只需要找出一条即可,DFS只关心路径存在与否,不在乎其长短。
1.2 DFS思想
从起点出发,在规定的方向中,选择一个可选方向,不断向前走,直到无法继续为止;
然后尝试另外一种方向,直到最后走到终点。
1.3 代码实现(栈!应获得C位)
递归实现:
- 判断是否抵达了目的地B,是,则立即返回
- 标记当前点已被访问过(直接修改输入数据为-1,不需额外空间辅助记忆,工作中不推荐这样)
- 在规定的四个方向上进行尝试
- 如果有一条路径被找到了,则返回true
bool dfs(vector<vector<int>>& maze,int x,int y){
if(x==B[0] && y==B[1]){
return true;
}
maze[x][y]=-1;
for(int d=0;d<4;++d){
int i=x+dx[d],j=y+dy[d];
if(isSafe(mase,i,j) && dfs(maze,i,j)){
return true;
}
}
return false;
}
非递归实现:
- 创建一个stack,用来将要被访问的点压入以及弹出
- 将起始点压入stack,并标记它为访问过
- 只要stack不为空就不断循环,处理每个点
- 从堆栈取出当前要处理的点
- 判断是否抵达了目的地B,是就返回true
- 如果不是目的地,就从四个方向上去尝试
- 将各个方向上的点压入堆栈,并把标记为访问过
- 尝试了所有可能还没找到B,则返回false
bool dfs(vector<vector<int>> &maze,int x,int y){
stack<vector<int>> si;
si.push_back({x,y});
maze[x][y] = -1;
while(!si.empty()){
vector<int> pos = si.pop_back();
x = pos[0];y = pos[1];
if(x==B[0] && y==B[1]){
return true;
}
for(int d=0;d<4;++d){
int i=x+dx[d],j=y+dy[d];
if(isSafe(maze,i,j)){
si.push_back({i,j});
maze[i][j] = -1;
}
}
}
return false;
}
1.4 复杂度分析
针对邻接表(图里有V个定点,E条边):
访问所有顶点的时间为O(V),查找所有定点邻居的时间为O(E),所以总的时间复杂度是O(V+E)。
针对邻接矩阵(图里有V个定点,E条边):
查找每个顶点的邻居要O(V)时间,查找整个矩阵的时候需要O(V2)的时间
例如:利用DFS在迷宫里找一条路径
迷宫一般用矩阵表示,所以假设它是一个M行N列的邻接矩阵
时间复杂度为O(MN):因为以供有MN个顶点
空间复杂度也是O(MN):DFS需要堆栈来辅助,在最坏的情况下所有顶点都被压入堆栈,所以它的空间复杂度是O(V),即O(MN)
1.5 找最短路径,算法优化
暴力法:
找出所有路径,比较选最短
优化法:
一边寻找目的地,一边记录它和起始点的距离(步数)
当发现从某个方向过来所需要的步数更少,则更新到这个点的步数
如果发现步数更多,则不再尝试
void solve(vector<vector<int>> maze){
for(int i=0;i<maze.size();++i){
for(int j=0;j<maze[0].size();++j){
if(maze[i][j]==0 && !(i==A[0] && j==A[1])){
maze[i][j] = max_value;
}
}
}
dfs(maze,A[0],A[1]);
if(maze[B[0]][B[1]] < max_value){
print("Shortest path count is:"+maze[B[0]][B[1]]);
}else{
print("Cannot find B!");
}
}
void dfs(vector<vector<int>> &maze,int x,int y){
if(x==B[0] && y==B[1]){
return true;
}
for(int d=0;d<4;++d){
int i=x+dx[d],j=y+dy[d];
if(isSafe(maze,i,j) && maze[i][j]>maze[x][y]+1){
maze[i][j] = maze[x][y]+1;
dfs(maze, i, j);
}
}
}
2 广度优先搜索算法(BFS)
2.1 解决问题
BFS一般用来解决最短路径问题
BFS从起始点开始出发,一层一层进行
每层当中的点距离起始点的步数都是相同的
2.2 双端BFS
同时从起点和终点开始的,广度优先的搜索,就是双端BFS
双端BFS可以大大提高搜索效率
例如判断社交应用程序中两个人之间需要经过多少朋友介绍才能互相认识
2.3 实现方法(队列,C位C位!)
运用广度优先搜索在迷宫中寻找最短路径
void bfs(vector<vector<int>> &maze,int x,int y){
deque<vector<int>> dqvi;//创建一个队列
dqvi.push_back({x,y});//将起始点加入队列中
while(!dqvi.empty()){//只要队列不为空,就一直循环下去
vector<int> pos = dqvi.top();//从队列中取出当前要处理的点
x = pos[0];y=pos[1];
for(int d=0;d<4;++d){//在四个方向上进行BFS搜索
int i=x+dx[d],j=y+dy[d];
if(isSafe(maze,i,j)){//判断一下该方向上的点是否已经访问过了
maze[i][j] = maze[x][y]+1; //被访问过,则记录步数,并加入队列中
dqvi.push_back({i,j});
if(i==B[0] && j==B[1])//找到目的地后立即返回
return;
}
}
}
}
2.4 复杂度分析
邻接表(V个顶点,E条边):
每个顶点都访问一次,时间复杂度O(V),访问顶点的时候,与它项链的顶点也都要被访问一次,即O(E),整体时间复杂度为O(V+E)。
邻接矩阵(V个顶点,E条边):
每次都要检查每个顶点与其他顶点是否有联系,O(V2)。
BFS迷宫找路径:
迷宫看做矩阵,是个M行N列的邻接矩阵
时间复杂度为O(MN),最坏情况下空间复杂度O(MN)
上一篇: 深度和广度优先算法
下一篇: HashMap底层原理(转帖)