目录

  • 深搜
    • 200. 岛屿数量
    • 695. 岛屿的最大面积
    • 130. 被围绕的区域
    • 547. 省份数量
    • 417. 太平洋大西洋水流问题
  • 回溯
  • 广搜
    • 111. 二叉树的最小深度
    • 752. 打开转盘锁
  • 深搜与广搜结合
    • 934. 最短的桥

深搜

深搜DFS,在搜索到一个新节点时,立即对该新节点进行遍历,需要用到栈实现,或者使用与栈等价的递归实现。
深搜也可以用来检测环路:记录每个遍历过的节点的父节点,若有一个节点被再次遍历且父节点不同,则说明有环。
有时我们可能会需要对已经搜索过的节点进行标记,以防止在遍历时重复搜索某个节点,这种做法叫做状态记录或者记忆化。

200. 岛屿数量

class Solution {public:void dfs(vector<vector<char>>& grid,int m,int n,int i,int j){//如果越界或者为海域,退出if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') return;//标记为海域grid[i][j] = '0';dfs(grid,m,n,i-1,j);dfs(grid,m,n,i+1,j);dfs(grid,m,n,i,j-1);dfs(grid,m,n,i,j+1);return ;}int numIslands(vector<vector<char>>& grid) {int m = grid.size();int n = grid[0].size();int time = 0;for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(grid[i][j] == '1'){dfs(grid,m,n,i,j);time++;}}}return time;}
};

695. 岛屿的最大面积

一般来说,深搜可以分为主函数和辅助函数。
主函数用于遍历所有的所有搜索位置,判断是否可以开始搜索,如果可以即用辅助函数进行搜索。
辅助函数负责深搜的递归调用或者(栈)实现。

class Solution {public:vector<int> direction {-1,0,1,0,-1};//辅助函数int dfs(vector<vector<int>>& grid, int r, int c){if(grid[r][c] == 0) return 0;//否则清除标志grid[r][c] = 0;//此时岛屿已经有1面积了,接下来进行对四个方向进行深搜int x,y,area = 1;for(int i = 0; i < 4; i++){//新起点x = r + direction[i];y = c + direction[i+1];//如果不越界,则继续深搜if(x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size())area += dfs(grid,x,y);}//最后返回以r,c为起点的岛屿面积,并且在地图上清除这个岛屿,防止多次搜索。return area;}//主函数int maxAreaOfIsland(vector<vector<int>>& grid) {if(grid.empty() || grid[0].empty()) return 0;int max_area = 0;for(int i = 0; i < grid.size(); i++){for(int j = 0; j < grid[0].size(); j++){if(grid[i][j] == 1){max_area = max(max_area,dfs(grid,i,j));}}}return max_area;}
};

辅助函数还可以写成这样,我更倾向于这种写法:

//辅助函数
int dfs(vector<vector<int>>& grid, int r, int c)
{//如果该点越界或者为海域,退出递归if(r < 0 || r >= grid.size() || c < 0 || c >= grid[0].size() || grid[r][c] == 0) return 0;//否则说明该点为岛屿,面积+1,同时清除记录grid[r][c] = 0;//返回以该点搜索起点的面积结果return 1 + dfs(grid,r-1,c) + dfs(grid,r,c+1) + dfs(grid,r+1,c) + dfs(grid,r,c-1);
}

130. 被围绕的区域

class Solution {public:void dfs(vector<vector<char>>& board,int m,int n,int i,int j){//递归退出条件if(i < 0 || i >= m || j < 0 || j >= n || board[i][j] == 'X' || board[i][j] == 'A') return;//否则进行标记,然后继续深搜board[i][j] = 'A';dfs(board,m,n,i-1,j);dfs(board,m,n,i+1,j);dfs(board,m,n,i,j-1);dfs(board,m,n,i,j+1);return;}void solve(vector<vector<char>>& board) {int m = board.size();int n = board[0].size();//用dfs将边界的相连的0全部置为Afor(int i = 0; i < m ; i++){dfs(board,m,n,i,0);dfs(board,m,n,i,n-1);}for(int j = 0; j < n ; j++){dfs(board,m,n,0,j);dfs(board,m,n,m-1,j);}//进行覆盖操作,只有为A的不进行覆盖for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(board[i][j] == 'A') board[i][j] = 'O';else board[i][j] = 'X';}}return;}
};

547. 省份数量

分析:求无向图中的连通域个数,输入矩阵即为无向图的邻接矩阵

深搜思路:遍历所有城市,对于每个城市,如果该城市没有被访问过,则从该城市开始深度优先搜索,通过矩阵得到与该城市直接相连的城市有哪些,这些城市与该城市属于同一个连通分量,然后对这些城市继续深搜,直到同一个连通分量的所有城市都被访问到,就可以得到一个省份。

class Solution {public:void dfs(vector<vector<int>>& isConnected,int i,vector<bool>& visited) {for(int j = 0; j < isConnected.size(); j++){//继续遍历与顶点相邻的顶点,使用visited数组防止重复访问if(isConnected[i][j] == 1 && visited[j] == false){//标记当前访问过的顶点visited[j] = true;dfs(isConnected,j,visited);}}return;}int findCircleNum(vector<vector<int>>& isConnected) {int n = isConnected.size();int count = 0;//标识顶点是否被访问vector<bool> visited(n,false);for(int i = 0; i < n; i++){//如果当前顶点没有被访问,说明是一个新的连通域,遍历这个连通域且计数+1if(!visited[i]){dfs(isConnected,i,visited);count++;}}return count;}
};

417. 太平洋大西洋水流问题

1、边界可以去往旁边的一个海洋
也就是说边界与海洋是连通的,所以需要从外往内扩散,获得与边界连通的区域(也就是高度相等或者高度比当前区域高的位置)
2、为了防止重复遍历,添加visited数组
3、将连通的部分放到两个子集中,一个是和太平洋连通的区域,一个是和大西洋连通的区域。然后对两个求交集即可。

class Solution {public:vector<int> direction{-1,0,1,0,-1};bool Isvaild(int x,int y,vector<vector<int>>& matrix){if(x >= 0 && x < matrix.size() && y >= 0 && y < matrix[0].size()) return true;else return false;}//辅函数void dfs(vector<vector<int>>& matrix,vector<vector<bool>>& can_reach,int r,int c){//如果这个点遍历过了,退出if(can_reach[r][c]) return;can_reach[r][c] = true;int x,y;//向四个方向扩散for(int i = 0; i < 4; i++){x = r + direction[i];y = c + direction[i+1];if(Isvaild(x,y,matrix) && matrix[r][c] <= matrix[x][y])dfs(matrix,can_reach,x,y);}}//主函数vector<vector<int>> pacificAtlantic(vector<vector<int>>& matrix){if(matrix.empty() || matrix[0].empty()) return {};vector<vector<int>> ans;int m = matrix.size();int n = matrix[0].size();//记录能与p洋连通的区域vector<vector<bool>> can_reach_p(m,vector<bool>(n,false));//记录能与a洋连通的区域vector<vector<bool>> can_reach_a(m,vector<bool>(n,false));//遍历上下边界for(int i = 0; i < m; i++){//扩散深搜dfs(matrix,can_reach_p,i,0);dfs(matrix,can_reach_a,i,n-1);}//遍历左右边界for(int j = 0; j < n; j++){//扩散深搜dfs(matrix,can_reach_p,0,j);dfs(matrix,can_reach_a,m-1,j);}//将所有遍历结果进行取交集for(int i = 0; i < m; i++){for(int j = 0; j < n; j++){if(can_reach_p[i][j] && can_reach_a[i][j])ans.push_back(vector<int>{i,j});}}return ans;}
};

回溯

回溯之前,已经做过一些题目了,都是一些经典题目,直接放在这儿。
算法题复习(回溯)

广搜

模板如下:

//计算从起点start到终点target的最短距离
int BFS(Node start,Node target)
{queue<Node> q;   //核心数据结构Set<Node> visited; //避免走回头路q.push(start);     //将起点加入队列visited.insert(start);int steps = 0;  //记录扩散步数while(!q.empty()){//更新步数step++;int sz = q.size();//将当前队列中的所有节点向四周扩散for(int i = 0; i < sz; i++){Node cur = q.front();q.pop();//判断是否到达终点if(cur == target) return step;//将cur相邻的节点加入队列for(Node x: cur.adj()){//如果没有遍历过if(visited.find(x) == visited.end()){q.push(x);visited.insert(x);}}}}
}

cur.adj表示与cur相邻的节点。visited主要是防止走回头路,二叉树结构没有子节点到父节点的指针,所以不会走回头路不需要visited。
不同于深度优先搜索,广搜是一层一层遍历的,因此需要用到先入先出的队列,常常用来处理最短路径问题。

深搜和广搜都可以处理可达性问题,即从一个节点开始是否能到达另一个节点。因为深搜可以利用递归快速实现,所以很多人习惯使用深搜。但是实际工程中,很少使用递归,容易产生栈溢出。

111. 二叉树的最小深度

start:root根节点
target:最靠近根节点的的叶子节点

if(cur.left == nullptr && cur.right == nullptr) //到达了叶子节点
class Solution {public:int minDepth(TreeNode* root) {int result = 0;queue<TreeNode*> que;if(root == nullptr) return 0;que.push(root);while(!que.empty()){result++;int sz = que.size();for(int i = 0; i < sz; i++){TreeNode* cur = que.front();que.pop();//如果走到终点,返回结果if(cur->left == nullptr && cur->right == nullptr) return result;//否则继续if(cur->left != nullptr) que.push(cur->left);if(cur->right != nullptr) que.push(cur->right);}}return result;}
};

752. 打开转盘锁

计算从初始状态“0000”拨出目标状态的最少次数,如果永远无法拨出,则返回-1.
列表 deadends 包含了一组死亡数字,一旦拨轮的数字和列表里的任何一个元素相同,这个锁将会被永久锁定,无法再被旋转。
从0000开始,按照广度的思想,转一次有8种可能:
1000、9000、0100、0900、…
可以抽象成一幅图,每个节点有8个相邻的节点,求最短距离,此时可以用上BFS。
使用下面的初步代码,是按照BFS来寻找的最小密码组合,不过显然是有问题的。

class Solution {public:string plusOne(string s,int j){string result = s;if(result[j] == '9') result[j] = '0';else result[j] = result[j] + 1;return result;}string minusOne(string& s,int j){string result = s;if(result[j] == '0') result[j] = '9';else result[j] = result[j] - 1;return result;}int openLock(vector<string>& deadends, string target) {queue<string> q;string start = "0000";q.push(start);int time = 0;while(!q.empty()){time++;int sz = q.size();for(int i = 0; i < sz; i++){string cur = q.front();q.pop();//判断是否到达终点if(cur == target) return time;//将一个节点相邻的节点加入队列for(int j = 0; j < 4; j++){string up = plusOne(cur,j);q.push(up);string down = minusOne(cur,j);q.push(down);}}}return time;}
};

存在的问题如下:
1、会走回头路,比如"0000"拨到"1000",但是等从队列中拿出"1000",还会拨出"0000"
2、没有对deadends进行处理,按道理来说,这些死亡密码不能出现,遇到这些密码的时候需要跳过。
修改后的代码如下:

class Solution {public:string plusOne(string s,int j){string result = s;if(result[j] == '9') result[j] = '0';else result[j] = result[j] + 1;return result;}string minusOne(string& s,int j){string result = s;if(result[j] == '0') result[j] = '9';else result[j] = result[j] - 1;return result;}int openLock(vector<string>& deadends, string target) {//记录需要跳过的死亡密码set<string> deads;for(string s : deadends) deads.insert(s);//记录已经穷举过的密码,防止走回头陆set<string> visited;queue<string> q;string start = "0000";q.push(start);visited.insert(start);int time = 0;while(!q.empty()){int sz = q.size();for(int i = 0; i < sz; i++){string cur = q.front();q.pop();//判断是否合法if(deads.find(cur) != deads.end()) continue; //判断是否到达终点if(cur == target) return time;//将一个节点相邻的节点加入队列,如果是存在过的就不需要加入队列了for(int j = 0; j < 4; j++){string up = plusOne(cur,j);if(visited.find(up) == visited.end()){q.push(up);visited.insert(up);}string down = minusOne(cur,j);if(visited.find(down) == visited.end()){q.push(down);visited.insert(down);}}}time++;}return -1;}
};

深搜与广搜结合

934. 最短的桥

class Solution {public:vector<int> direction{-1,0,1,0,-1};void dfs(vector<vector<int>>& A,queue<pair<int,int>>& points,int m,int n,int i,int j){//如果越界了或者遍历过了,不继续向深处遍历if(i < 0 || j < 0 || i >= m || j >= n || A[i][j] == 2) return;//如果是海域,插入队列中if(A[i][j] == 0){points.push({i,j});return ;}A[i][j] = 2;//继续深度遍历dfs(A,points,m,n,i-1,j);dfs(A,points,m,n,i+1,j);dfs(A,points,m,n,i,j-1);dfs(A,points,m,n,i,j+1);return;} int shortestBridge(vector<vector<int>>& A) {int m = A.size();int n = A[0].size();//深度遍历,寻找第一个岛屿int flag = 0;queue<pair<int,int>> points;for(int i = 0; i < m; i++){if(flag == 1) break;for(int j = 0; j < n; j++){//深度遍历完一个岛屿后,立即退出if(A[i][j] == 1){flag = 1;dfs(A,points,m,n,i,j);break;}}}//开始广度遍历int level = 0;while(!points.empty()){int N = points.size();level++;while(N--){auto [r,c] = points.front();points.pop();//向四周广度遍历for(int k = 0; k < 4; k++){int x = r + direction[k];int y = c + direction[k+1];if(x >= 0 && x < m && y >= 0 && y < n){//如果还是第1个岛屿if(A[x][y] == 2) continue;if(A[x][y] == 1) return level;//如果还是0,则是继续入队列points.push({x,y});//将该坐标并入第1个岛屿A[x][y] = 2;}}}}return level;}
};

《搜索算法——DFS、BFS、回溯》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. auuc 评估指标_分类之性能评估指标
  2. Android——SQLite实现面向对象CRUD
  3. php中的div是什么意思,div是什么意思?div标签怎么用
  4. python opencv 录制视频_OpenCV Python 录制视频
  5. gulp html 压缩,gulp-gzip压缩
  6. 如何设置 jqplot 图表插件的标题图例和直线
  7. 简述驱动桥的动力传递路线_卡车驱动桥中的差速锁究竟有多大的作用?它是如何起作用的?...
  8. java 字符串是对象吗_解析Java中的String对象的数据类型
  9. const char * 类型的实参与 char * 类型的形参不兼容_4 种 C++ 强制类型转换,你都清楚吗?...
  10. datatable中使用linq的条件或_C# 10. LINQ 的三种查询语句写法
  11. 机器视觉 光学工程专业_机器视觉,如何影响世界?光学筛选机技术挑起大梁!...
  12. 团队作业第六次-团队Github实战训练
  13. 初识Redis educoder
  14. 小程序坑 redirectTo 计时器 setInterval clearInterval
  15. 《Python核心编程》笔记 基础
  16. 监控网页变化,实时推送微信消息
  17. 使用wav2sbc.exe无法转换WAV文件问题
  18. 如何隐藏电脑下方工具栏个别图标_小编教你电脑如何隐藏任务栏图标
  19. 基于Tableau的疫情数据可视化看板
  20. 新手gxf学python---万年历

热门文章

  1. java mvc 案例_springmvc经典案例
  2. java arraylist 重复_Java中ArrayList去除重复元素
  3. Webpack 2 视频教程 002 - NodeJS 安装与配置
  4. easy html css tree 简单的HTML css导航树
  5. 服务器端如何开启GZIP压缩功能
  6. JavaScript中不得不说的断言?
  7. Bootstrap中的圆角图片效果
  8. CSS Hack 汇总速查一览
  9. 一些关于Viewport与device-width的东西~(转)
  10. Laravel 不同环境加载不同的.env文件