热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

5.搜索dfs、回溯、bfs

*深度优先搜索(dfs)和广度优先搜索(bfs)是两种最常见的优先搜索方法,它们被广泛的运用在图和树等结构中进行搜索。主要使用的技术为:栈与递归、递归与回溯、队列**深度优先搜索(

/*
深度优先搜索(dfs)和广度优先搜索(bfs)是两种最常见的优先搜索方法,它们被广泛的运用在图和树等结构中进行搜索。
主要使用的技术为:栈与递归、递归与回溯、队列
*/
/*
深度优先搜索(depth-first search,DFS):主要使用“栈与递归”的算法
在搜索到一个新的节点时,立即对该节点进行遍历;因此遍历需要先入后出的栈来实现,也可以通过与栈等价的递归来实现。
*/
//695.最大岛屿面积
/*
题目描述:给定一个二维的0-1矩阵,其中0表示海洋,1表示陆地。单独的或相邻的陆地可以形成岛屿,每个格子只与其上下左右四个格子
相邻。求最大的岛屿面积。
输入输出:输入是一个二维数组,输出是一个整数,表示最大岛屿面积。
Input:
[[1,0,1,1,0,1,0,1],
[1,0,1,1,0,1,1,1],
[0,0,0,0,0,0,0,1]]
Output:6
题解:递归调用,辅函数为深度优先的递归写法,找到陆地,查看陆地上下左右是否为陆地,如果是则岛屿面积+1,并记得沉没此块陆地,防止被重复搜索到。
*/
int maxAreaOfIsland(vector>& grid) {
if(grid.empty() || grid[0].empty()) return 0;
int max_area = 0;
for(int i=0; i for(int j=0; j max_area = max(max_area, dfs(grid, i, j));
}
}
return max_area;
}
int dfs(vector>& grid, int i, int j){
if(i<0 || i>=grid.size() || j<0 || j>=grid[0].size() || grid[i][j]==0){
return 0;
}
grid[i][j]=0; //沉没此块陆地
return 1+dfs(grid, i-1, j)+dfs(grid, i, j-1)+dfs(grid, i+1, j)+dfs(grid, i, j+1); //寻找周围相连陆地
}
//547.省份数量
/*
题目描述:有n个城市,其中一些彼此相连,另一些没有相连。如果城市a与城市b直接相连,且城市b与城市c直接相连,那么城市a与城市c间接相连。
省份是一组直接或间接相连的城市,组内不含其他没有相连的城市。 给你一个n*n的矩阵isConnected, 其中isConnected[i][j]=1,表示第i个
城市和第j个城市直接相连,而isConnected[i][j]=0表示二者不直接相连。返回矩阵中省份的数量。
和最大岛屿面积一摸一样啊。
*/
//主函数
int findCircleNum(vector>& isConnected){
int n=isConnected.size(), count=0;
vector visited(n, false);
for(int i=0; i if(!visited[i]){
dfs(isConnected, i, visited);
//一趟dfs后,所有与i相连的城市就都找到了
count++;
}
}
return count;
}
//辅函数
void dfs(vector>& isConnected, int i, vector& visited){
visited[i]=true;
for(int j=0;j //如果城市i与城市j相连,并且j没有被访问过,那就进行递归找到与j相连的城市,最终vistied为与i可以访问的所有城市
if(isConnected[i][j] == 1 && !visited[j]){
dfs(isConnected, j, visited);
}
}
}
/*
回溯法:https://www.bilibili.com/video/BV1cy4y167mM 系列视频
回溯法(backtracking)是优先搜索的一种特殊情况,又称试探法(或穷举法、暴力搜索),常用于需要记录节点状态的深度优先搜索。
通常来说,排列、组合、选择类问题使用回溯法比较方便。
顾名思义,回溯法的核心是回溯。在搜索到某一节点的时候,如果我们发现目前的节点(及其子节点)并不是需求目标时,我们回退到原来的节点
继续搜索,并且把在目前节点修改的状态还原。这样的好处是我们可以始终只对图的总状态进行修改,而非每次遍历时新建一个图来储存状态。
*/
//46.全排列:https://www.bilibili.com/video/BV1dx411S7WR?from=search&seid=6705623231078872903
/*
题目描述:给定一个无重复数字的整数数组,求其所有的排列方式。
Input:[1,2,3]
Output:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
可以以任意顺序输出,只包含了所有排列方式即可。
题解:
怎样输出所有的排列方式呢?对于每一个当前位置i,我们可以将其与之后的任意位置交换,然后继续处理i+1,直到处理到最后一位。为了防止
我们每次遍历时都要新建一个子数组存储位置i之前已经交换好的数字,我们可以利用回溯法,只对原数组进行修改,在递归完成后再修改回来。
我们以样例[1,2,3]为例,按照这种方法,我们输出的数组顺序为[[1,2,3][1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]],可以看到
所有的排列在这个算法中都被考虑到了。
*/
//主函数
vector> permute(vector& nums) {
vector> ans;
backtracking(nums, 0, ans);
return ans;
}
//辅函数
void backtracking(vector &nums, int level, vector> &ans){
if(level == nums.size()-1){ //for循环结束就把排列后的nums push_back到ans里
ans.push_back(nums);
return;
}
for(int i=level; i swap(nums[i], nums[level]); //把下标为i的数组提到前边(level)去
backtracking(nums, level+1, ans); //剩余的数组再做全排列
swap(nums[i], nums[level]); //回改当前节点状态,接着进行for循环
}
}
//77.组合
/*
题目描述:
给定一个整数n和一个整数k,求在1到n中选取k个数字的所有组合方法
输入输出样例:
Input: n=4, k=2
Output:[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]
这里二维数组的每个维度都可以以任意顺序输出
题解:类似于排列问题,我们也可以进行回溯。排列回溯的是交换的位置,而组合回溯的是否把当前的数字加入结果中。
*/
//主函数
vector> combine(int n, int k){
vector> result;
vector path;
backtracting(result, path, n, k, 1);
return result;
}
void backtracting(vector>& result,vector& path,int n,int k, int start){
//终止条件
if(path.size() == k){
result.push_back(path);
return; //仅跳出当前回溯分支
}
//for循环
for(int i=start; i<=n; i++){
path.push_back(i); //子队列赋值
backtracting(result, path, n, k, i+1); //递归
path.pop_back();
}
}
//此题n=4,k=2 可以用两个for循环就能解决,如下
for(int i=1; i<=4; i++){
for(int j=i+1; j<=4; j++){
cout < }
}
/*
那么为什么还会用到回溯的,我们想下,如果k=3,那么我们是不是还要加一个for循环
如果n=100,k=50呢,写50个for循环......?
那么用回溯就能轻松解决,只需改下传递的参数就可以喽
是不是瞬间感觉这些算法还是很有用的?
*/
//79.单词搜索
/*
题目描述:给定一个字母矩阵,所有的字母都与上下左右四个方向上的字母相连。给定一个字符串,求字符串能不能在字母矩阵中寻找到。
和岛屿面积有点像
输入输出样例:
输入是一个二维字符数组和一个字符串,输出是一个布尔值,表示字符串是否可以被寻找到。
Input: word="ABCCED",board=
[['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']]
Output:true
从坐上角‘A’开始,我们可以先向右、再向下、最后向左,找到连续的“ABCCED”
不同于排列组合问题,本题采用的并不是修改输出方式,而是修改访问标记。在我们对任意位置进行深度优先搜索时,我们先标记当前位置为已访问,
以避免重复遍历(如防止向右搜索后又向左返回);在所有的可能都搜索完成后,再改回当前位置为未访问,防止干扰其他位置搜索到当前位置。
使用回溯法,我们可以只对一个二维的访问矩阵进行修改,而不用把每次的搜索状态作为一个新对象传入递归函数中。
*/
bool exist(vector>& board, string word) {
if(board.empty()) return false;
int m=board.size(),n=board[0].size();
vector> visitied(m,vector(n,false)); //用来标记当前点,状态矩阵
bool find=false;
for(int i=0; i for(int j=0; j backtracting(i, j, board, word, visitied, 0, find);
}
}
return find;
}
void backtracting(int i, int j, vector>& board, string& word, vector>& visitied, int pos, bool &find){
//条件判断
if(i<0 || i>=board.size() || j<0 || j>= board[0].size()){ //判断是否越界
return;
}
if(find || visitied[i][j] || board[i][j]!=word[pos]){ //判断是否是否匹配
return;
}
if(pos==word.size()-1){ //是否匹配完成
find=true;
return;
}
visitied[i][j]=true; //匹配成功,沉没此点
//上下左右递归
backtracting(i-1, j, board, word, visitied, pos+1, find);
backtracting(i+1, j, board, word, visitied, pos+1, find);
backtracting(i, j-1, board, word, visitied, pos+1, find);
backtracting(i, j+1, board, word, visitied, pos+1, find);
visitied[i][j]=false; //匹配成功,恢复沉没点
}
//51.N(8)-皇后
/*hard*/
/*
题目描述:给定一个大小为n的正方形国际象棋棋盘,求有多少种方式可以放置n个皇后并使得她们互不攻击,即每一行、列、左斜、右斜
最多只有一个皇后。
输入输出:输入是一个整数n,输出是一个二维字符串数组,表示所有的棋盘表示方法。
题解:类似于在矩阵中寻找字符串,本题也是通过修改状态矩阵来进行回溯。不同的是,我们需要对每一行、列、左斜、右斜建立访问数组,
来记录它们是否存在皇后。本题有一个隐藏的条件,即满足条件的结果中每一行或列有且仅有一个皇后。这是因为我们一共只有n行和n列。
所以如果我们通过对每一行遍历来插入皇后,我们就不需要对行建立访问数组了。
*/
//主函数
vector> solveNQueens(int n){
vector> ans; //结果
if(n == 0){
return ans;
}
vector board(n, string(n, '.')); //string 数组 初始化 棋盘
vector column(n,false), ldiag(2*n-1, false), rdiag(2*n-1, false); //列,左斜,右斜
backtracking(ans, board, column, ldiag, rdiag, 0, n);
return ans;
}
//辅函数
void backtracking(vector> &ans, vector &board, vector &column,
vector& ldiag, vector &rdiag, int row, int n){
//返回条件
if(row == n){
ans.push_back(board);
return;
}
for(int i=0; i if(column[i] || ldiag[n-row+i-1] || rdiag[row+i+1]) {
continue;
}
//修改当前节点状态
board[row][i] = 'Q';
column[i] = ldiag[n-row+i-1] = rdiag[row+i+1] = true;
//递归子节点
backtracking(ans, board, column, ldiag, rdiag, row+1, n);
//回改当前节点状态
board[row][i]='.';
column[i] = ldiag[n-row+i-1] = rdiag[row+i+1] = false;
}
}
//bfs 广度优先搜索 主要使用队列queue“先进先出”来处理 处理二叉树问题用的较多,之后的二叉树章节会有更多题目
/*
广度优先搜索不同于深度优先搜索,它是一层层进行遍历的,因此需要用先入先出的队列而非先入后出的栈进行遍历。由于是按层进行遍历,广度优先搜索时按照
“广”的方向进行遍历的,也常常用来处理最短路径等问题。
考虑如下一颗简单的树。我们从1号节点开始遍历,假如遍历顺序是从左子节点到右子节点,那么按照优先向着“广”的方向前进的策略,队列顶端的元素变换过程
[1]->[2->3]->[4],其中方括号代表每一层的元素。
*/
//934.最短的桥
/*
题目描述:给定一个二维0-1矩阵,其中1表示陆地,0表示海洋,每个位置与上下左右相连。已知矩阵中有且只有两个岛屿,求最少要填海造陆多少个位置才可以将两个
岛屿相连。
输入输出样例:输入是一个二维数组,输出是一个非负整数,表示需要填海造陆的位置数。
Input:
[[1,1,1,1,1]
[1,0,0,0,1]
[1,0,1,0,1]
[1,0,0,0,1]
[1,1,1,1,1]]
Output:1
题解:本题实际上是求两个岛屿间的最短距离,因此我们可以先通过任意搜索方法找到其中一个岛屿,然后利用广度优先搜索查找与另一个岛屿的最短距离。
*/
vector direction{-1, 0, 1, 0, -1};
//主函数
int shortestBridge(vector>& grid) {
int m=grid.size(), n=grid[0].size();
queue

> points; //广度优先遍历会用队列queue来解决这类问题
//dfs寻找第一个岛屿,并把1全部赋值为2
bool flipped=false; //
for(int i=0; i if(flipped) break;
for(int j=0; j if(grid[i][j] == 1){
dfs(points, grid, m, n, i, j);
flipped=true;
break;
}
}
}
//bfs寻找第二个岛屿,并把过程中经过的0赋值为2
int x,y;
int level=0;
while(!points.empty()){
++level;
int n_points=points.size();
while(n_points--){
auto [r,c] = points.front();
points.pop();
for(int k=0; k<4; k++){
x=r+dirction[k], y=c+dirction[k+1];
if(x>=0 && y>=0 && x if(grid[x][y]==2){
continue;
}
if(grid[x][y]==1){
return level;
}
points.push({x,y});
grid[x][y] = 2;
}
}
}
}
return 0;
}
//辅函数
void dfs(queue

>& points, vector>& grid, int m, int n, int i, int j){
if(i<0 || i>=m || j<0 || j>=n || grid[i][j]==2){
return;
}
if(grid[i][j]==0){
points.push({i,j});
return;
}
grid[i][j]==2;
dfs(points, grid, m, n, i-1, j);
dfs(points, grid, m, n, i+1, j);
dfs(points, grid, m, n, i, j-1);
dfs(points, grid, m, n, i, j+1);
}

 



推荐阅读
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • Learning to Paint with Model-based Deep Reinforcement Learning
    本文介绍了一种基于模型的深度强化学习方法,通过结合神经渲染器,教机器像人类画家一样进行绘画。该方法能够生成笔画的坐标点、半径、透明度、颜色值等,以生成类似于给定目标图像的绘画。文章还讨论了该方法面临的挑战,包括绘制纹理丰富的图像等。通过对比实验的结果,作者证明了基于模型的深度强化学习方法相对于基于模型的DDPG和模型无关的DDPG方法的优势。该研究对于深度强化学习在绘画领域的应用具有重要意义。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 也就是|小窗_卷积的特征提取与参数计算
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了卷积的特征提取与参数计算相关的知识,希望对你有一定的参考价值。Dense和Conv2D根本区别在于,Den ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 浏览器中的异常检测算法及其在深度学习中的应用
    本文介绍了在浏览器中进行异常检测的算法,包括统计学方法和机器学习方法,并探讨了异常检测在深度学习中的应用。异常检测在金融领域的信用卡欺诈、企业安全领域的非法入侵、IT运维中的设备维护时间点预测等方面具有广泛的应用。通过使用TensorFlow.js进行异常检测,可以实现对单变量和多变量异常的检测。统计学方法通过估计数据的分布概率来计算数据点的异常概率,而机器学习方法则通过训练数据来建立异常检测模型。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • PDO MySQL
    PDOMySQL如果文章有成千上万篇,该怎样保存?数据保存有多种方式,比如单机文件、单机数据库(SQLite)、网络数据库(MySQL、MariaDB)等等。根据项目来选择,做We ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有