【算法学习】递归、搜索与回溯算法(二)

算法学习:

https://blog.csdn.net/2301_80220607/category_12922080.html?spm=1001.2014.3001.5482

前言:

在(一)中我们挑了几个经典例题,已经对递归、搜索与回溯算法进行了初步讲解,今天我们来进一步讲解这几个算法知识点,主要是进行了一些拔高,比如引入了剪枝的操作,来看今天的例题吧

目录

1. 经典例题

1.1 全排列 ||

1.2 组合总和

1.3 N皇后

1.4 有效的数独

1.5 单词搜索

1.6 不同路径 |||

2. 总结


1. 经典例题

1.1 全排列 ||

47. 全排列 II

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。

示例 1:

输入:nums = [1,1,2]
输出:
[[1,1,2],[1,2,1],[2,1,1]]

示例 2:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

提示:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

算法原理:

这道题与(一)中的全排列|解法上区别不大,唯一不同的是这里需要有剪枝操作,因为全排列一中序列中的数字都不相同,这里序列中的数字是可以相同的

我们看上面这个示例,观察这个策略树发现有些分支上面画着x,这些就是错误分支,需要我们剪掉,其中橙红色的x代表的是同一个数被再次使用的错误分支,绿色的x代表的是同一层节点中相同元素被多次使用的错误分支
基于上面的剪掉错误分支的原理,我们的代码可以从两个角度切入,一个是:只关心不合法的分支;一个是:只关心合法的分支

代码实现:

class Solution {vector<vector<int>> ret;vector<int> path;bool check[10];
public:vector<vector<int>> permuteUnique(vector<int>& nums) {sort(nums.begin(),nums.end());dfs(nums);return ret;}void dfs(vector<int>& nums){if(path.size()==nums.size()){ret.push_back(path);return;}for(int i=0;i<nums.size();i++){//只关注不合法的分支,当是不合法的分支时,需要剪掉,所以不进入递归if(check[i]==true||(i!=0&&nums[i]==nums[i-1]&&check[i-1]==false))continue;//只关注合法的分支的做法就是将上面if中的条件相反,然后把下面的内容包在函数体内path.push_back(nums[i]);check[i]=true;dfs(nums);path.pop_back();check[i]=false;}}
};

1.2 组合总和

LCR 081. 组合总和

给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。

candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的唯一组合数少于 150 个。

示例 1:

输入: candidates = [2,3,6,7], target = 7<
输出: [[7],[2,2,3]]

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

示例 4:

输入: candidates = [1], target = 1
输出: [[1]]

示例 5:

输入: candidates = [1], target = 2
输出: [[1,1]]

提示:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • candidate 中的每个元素都是独一无二的。
  • 1 <= target <= 500

算法原理:

代码实现:

class Solution {vector<vector<int>> ret;vector<int> path;int sum;
public:vector<vector<int>> combinationSum(vector<int>& candidates, int target) {dfs(candidates,target,0);return ret;}void dfs(vector<int>& candidates,int target,int pos){if(sum>=target){if(sum==target) ret.push_back(path);return;}for(int i=pos;i<candidates.size();i++){sum+=candidates[i];path.push_back(candidates[i]);dfs(candidates,target,i);path.pop_back();sum-=candidates[i];}}
};

1.3 N皇后

51. N 皇后

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例 1:

输入:n = 4
输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:[["Q"]]

提示:

  • 1 <= n <= 9

算法原理:

解释:

这道题最关键的就是我们上面的剪枝操作,根据题意我们知道在一个nxn棋盘中,一个列中只能存在一个元素,所以我们可以创建一个bool数组col来标记列的元素情况我们把棋盘抽象到坐标系上,因为对角线上也只能有一个元素,对角线可以有如图的两种,这两种它们x+y都是一个定值,所以也可以创建bool数组来对它们进行标记

y-x可能为负数,负数不能作为bool数组的下标,所以可以加上一个n的偏移量

代码实现:

class Solution {bool checkCol[10],checkDigal1[20],checkDigal2[20];vector<vector<string>> ret;vector<string> path;int n;
public:vector<vector<string>> solveNQueens(int _n) {n=_n;path.resize(n);for(int i=0;i<n;i++)path[i].append(n,'.');dfs(0);return ret;}void dfs(int row){if(row==n){ret.push_back(path);return;}for(int col=0;col<n;col++)    //尝试在这一行放置皇后{//剪枝if(!checkCol[col]&&!checkDigal1[row-col+n]&&!checkDigal2[row+col]){path[row][col]='Q';checkCol[col]=checkDigal1[row-col+n]=checkDigal2[row+col]=true;dfs(row+1);//恢复现场path[row][col]='.';checkCol[col]=checkDigal1[row-col+n]=checkDigal2[row+col]=false;}}}
};

1.4 有效的数独

36. 有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

注意:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 空白格用 '.' 表示。

示例 1:

输入:board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:true

示例 2:

输入:board = 
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
输出:false
解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字(1-9)或者 '.'

算法原理:

解释:

这题不是回溯类的题,但是通过这题我们可以更好的理解N皇后那道题,这道题也是要保证数据在一定位置上不能重复出现,所以都可以采取哈希的方式(这里的数组模拟的就是哈希)进行标记,这道题要求同一行、同一列和同一个九宫格中都不能出现相同的方式所以我们就可以用三个bool数组来进行标记,我们拿第一个row[9][10]来举例,这表示的就是第i行0~9每个数字的存在情况,需要注意的是第三个数组,它用来标记每个九宫格里数字的出现情况,所以我们把整个大表格分为3x3九份,同时后面的【10】是用来记录各个数字出现情况,而且grid数组对应的下标其实就是【x/3】【y/3】

代码实现:

class Solution {bool col[9][10];bool row[9][10];bool grid[3][3][10];
public:bool isValidSudoku(vector<vector<char>>& board) {for(int i=0;i<9;i++){for(int j=0;j<9;j++){if(board[i][j]>='0'&&board[i][j]<='9'){int num=board[i][j]-'0';if(row[i][num] ||col[j][num]||grid[i/3][j/3][num]) return false;row[i][num]=col[j][num]=grid[i/3][j/3][num]=true;}}}return true;}
};

1.5 单词搜索

79. 单词搜索

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

示例 2:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true

示例 3:

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false

提示:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • board 和 word 仅由大小写英文字母组成

算法原理:

代码实现:

class Solution {bool vis[7][7];int m,n;
public:bool exist(vector<vector<char>>& board, string word) {m=board.size(),n=board[0].size();for(int i=0;i<m;i++)for(int j=0;j<n;j++){if(board[i][j]==word[0]){vis[i][j]=true;if(dfs(board,i,j,word,1)) return true;vis[i][j]=false;}}return false;}int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};bool dfs(vector<vector<char>>& board,int i,int j,string& word,int pos){if(pos==word.size()) return true;for(int k=0;k<4;k++){int x=i+dx[k], y=j+dy[k];if(x>=0 && x<m &&y>=0 &&y<n &&board[x][y]==word[pos] &&vis[x][y]==false){vis[x][y]=true;if(dfs(board,x,y,word,pos+1)) return true;vis[x][y]=false;}}return false;}
};

1.6 不同路径 |||

980. 不同路径 III

在二维网格 grid 上,有 4 种类型的方格:

  • 1 表示起始方格。且只有一个起始方格。
  • 2 表示结束方格,且只有一个结束方格。
  • 0 表示我们可以走过的空方格。
  • -1 表示我们无法跨越的障碍。

返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目

每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格

示例 1:

输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
输出:2
解释:我们有以下两条路径:
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2)
2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)

示例 2:

输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]]
输出:4
解释:我们有以下四条路径: 
1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3)
2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3)
3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3)
4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)

示例 3:

输入:[[0,1],[2,0]]
输出:0
解释:
没有一条路能完全穿过每一个空的方格一次。
请注意,起始和结束方格可以位于网格中的任意位置。

提示:

  • 1 <= grid.length * grid[0].length <= 20

这道题可以用动归来解决,但是难度比较大,用爆搜简单一点

这道题原理之类与前面几题很相似,我们把思路搞明白就很容易上手,根据题意,我们要做的是从1出发,遍历所有的0,然后到达2
我们可以把所有能够到达2的路径都尝试一遍,其中许多路径肯定是没有把0全部遍历的,这种的就需要被剪掉,我们可以采取一种更简单的方式,我们可以定义一个常量count用来记录我们所走过的0的个数,然后当我们走到终点时判断一下我们所走的0的个数与整个表格中0的个数是否一样,一样就代表我们把所有的0都遍历过了

代码实现:

class Solution {int dx[4]={1,-1,0,0};int dy[4]={0,0,-1,1};int step=0;int m,n;int ret=0;bool vis[20][20];
public:int uniquePathsIII(vector<vector<int>>& grid) {m=grid.size(),n=grid[0].size();int bx=0,by=0;for(int i=0;i<m;i++)for(int j=0;j<n;j++){if(grid[i][j]==0) step++;else if(grid[i][j]==1) bx=i,by=j;}step+=2;vis[bx][by]=true;dfs(grid,bx,by,1);return ret;}void dfs(vector<vector<int>>& grid,int i,int j,int path){if(grid[i][j]==2){if(step==path)ret++;return;}for(int k=0;k<4;k++){int x=i+dx[k],y=j+dy[k];if(x>=0&&x<m&&y>=0&&y<n&&grid[x][y]!=-1&&vis[x][y]==false){vis[x][y]=true;dfs(grid,x,y,path+1);vis[x][y]=false;}}}
};

2. 总结

上一篇主要讲解的是递归、搜索与回溯算法的一些基本知识和简单例题,本篇的一些题型结合深搜和宽搜的知识,还要用到一些剪枝的操作,总体来说难度大了很多,需要花费更多的时间在上面

本篇笔记:

感谢各位大佬观看,创作不易,还望各位大佬点赞支持!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79972.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTTP请求与缓存、页面渲染全流程

文章目录 前言**1. HTTP请求与缓存处理****缓存机制**• 强缓存&#xff08;Cache-Control / Expires&#xff09;• 协商缓存&#xff08;Last-Modified / ETag&#xff09; **2. 服务器响应与数据解析****3. HTML DOM 构建****4. CSSOM 构建****5. 渲染树&#xff08;Render …

限流算法学习笔记(一)Go Rate Limiter

文章目录 1. 背景与概述1.1 什么是速率限制1.2 Go Rate Limiter 的定义与价值 2. 核心思想与设计理念2.1 令牌桶算法的基本原理2.2 惰性评估设计2.3 多种处理策略的平衡2.4 简单易用的偶发控制 3. 架构设计与组件3.1 整体架构3.2 Limiter 组件3.3 Reservation 组件3.4 Limit 类…

n8n工作流自动化平台的实操:生成统计图的两种方式

1.成果展示 1.1n8n的工作流 牵涉节点&#xff1a;Postgres、Code、QuickChart、Edit Fields、HTTP Request 12.显示效果 2.实操过程 2.1节点说明 2.1.1Postgres节点&#xff1a; 注&#xff1a;将明细数据进行汇总。 2.1.2code节点&#xff1a; 注&#xff1a;将 查询的数…

JavaScript中数组和对象不同遍历方法的顺序规则

在JavaScript中&#xff0c;不同遍历方法的顺序规则和适用场景存在显著差异。以下是主要方法的遍历顺序总结&#xff1a; 一、数组遍历方法 for循环 • 严格按数组索引顺序遍历&#xff08;0 → length-1&#xff09; • 支持break和continue中断循环 • 性能最优&#xff0c;…

缓存(1):三级缓存

三级缓存是指什么 我们常说的三级缓存如下&#xff1a; CPU三级缓存Spring三级缓存应用架构&#xff08;JVM、分布式缓存、db&#xff09;三级缓存 CPU 基本概念 CPU 的访问速度每 18 个月就会翻 倍&#xff0c;相当于每年增⻓ 60% 左右&#xff0c;内存的速度当然也会不断…

Android setContentView()源码分析

文章目录 Android setContentView()源码分析前提setContentView() 源码分析总结 Android setContentView()源码分析 前提 Activity 的生命周期与 ActivityThread 相关&#xff0c;调用 startActivity() 时&#xff0c;会调用 ActivityThread#performLaunchActivity()&#xf…

uniapp自定义步骤条(可二开进行调试)

前言 有一个业务需求是需要一个步骤条&#xff0c;但是发现开源的都不太合适&#xff0c;所以就自己写了一个。 开始 test.vue <template><view class"authenticateRecordDetails_container"><!-- 进度 --><view class"authenticateSte…

22、近端策略优化算法(PPO)论文笔记

近端策略优化算法&#xff08;PPO&#xff09;论文笔记 一、研究背景与目标二、**方法****3.1 策略梯度基础****3.2 信任区域方法&#xff08;TRPO&#xff09;****3.3 剪切代理目标函数&#xff08;LCLIP&#xff09;****3.4 自适应KL惩罚系数****3.5 算法实现** 三、 L CLIP…

web 自动化之 Selenium 元素定位和浏览器操作

文章目录 一、元素定位的八大方法1、基于 id/name/class/tag_name 定位2、基于 a 标签元素的链接文本定位3、基于xpath定位4、css定位 二、浏览器操作1、信息获取2、 浏览器关闭3、 浏览器控制 一、元素定位的八大方法 web 自动化测试就是通过代码对网页进行测试&#xff0c;在…

前端面经 作用域和作用域链

含义&#xff1a;JS中变量生效的区域 分类&#xff1a;全局作用域 或者 局部作用域 局部作用域&#xff1a;函数作用域 和 块级作用域ES6 全局作用域:在代码中任何地方都生效 函数中定义函数中生效&#xff0c;函数结束失效 块级作用域 使用let或const 声明 作用域链:JS查…

【C/C++】RPC与线程间通信:高效设计的关键选择

文章目录 RPC与线程间通信&#xff1a;高效设计的关键选择1 RPC 的核心用途2 线程间通信的常规方法3 RPC 用于线程间通信的潜在意义4 主要缺点与限制4.1 缺点列表4.2 展开 5 替代方案6 结论 RPC与线程间通信&#xff1a;高效设计的关键选择 在C或分布式系统设计中&#xff0c;…

两种方法求解最长公共子序列问题并输出所有解

最长公共子序列&#xff08;Longest Common Subsequence, LCS&#xff09;是动态规划领域的经典问题&#xff0c;广泛应用于生物信息学&#xff08;如DNA序列比对&#xff09;、文本差异比对&#xff08;如Git版本控制&#xff09;等领域。本文将通过​​自顶向下递归记忆化​​…

SpringBoot应急知识学习系统开发实现

概述 一个基于SpringBoot开发的应急知识学习系统&#xff0c;该系统提供了完整的用户注册、登录、知识学习与测评功能。对于开发者而言&#xff0c;这是一个值得参考的免费Java源码项目&#xff0c;可以帮助您快速构建类似的教育平台。 主要内容 5.2 注册模块的实现 系统采…

【Python 字符串】

Python 中的字符串&#xff08;str&#xff09;是用于处理文本数据的基础类型&#xff0c;具有不可变性、丰富的内置方法和灵活的操作方式。以下是 Python 字符串的核心知识点&#xff1a; 一、基础特性 定义方式&#xff1a; s1 单引号字符串 s2 "双引号字符串" s…

第十六届蓝桥杯大赛软件赛C/C++大学B组部分题解

第十六届蓝桥杯大赛软件赛C/C大学B组题解 试题A: 移动距离 问题描述 小明初始在二维平面的原点&#xff0c;他想前往坐标(233,666)。在移动过程中&#xff0c;他只能采用以下两种移动方式&#xff0c;并且这两种移动方式可以交替、不限次数地使用&#xff1a; 水平向右移动…

如何使用极狐GitLab 软件包仓库功能托管 npm?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 软件包库中的 npm 包 (BASIC ALL) npm 是 JavaScript 和 Node.js 的默认包管理器。开发者使用 npm 共享和重用代码&#xff…

Matlab 基于Hough变换的人眼虹膜定位方法

1、内容简介 Matlab220-基于Hough变换的人眼虹膜定位方法 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

chili调试笔记14 画线 页面布置 线条导出dxf

2025-05-08 09-05-06 llm画线 页面布置 expand有自己的格式 删了就会按照子元素格式 不加px无效 没有指定尺寸设置100%无效 怎么把线条导出dxf command({name: "file.export",display: "command.export",icon: "icon-export", }) export class…

蓝绿发布与金丝雀发布

蓝绿发布与金丝雀发布 一、蓝绿发布&#xff1a;像「搬家」一样安全上线1. 生活化故事2. 技术步骤拆解步骤①&#xff1a;初始状态步骤②&#xff1a;部署新版本到绿环境步骤③&#xff1a;内部验证绿环境步骤④&#xff1a;一键切换流量步骤⑤&#xff1a;监控与回滚 3. 蓝绿发…

【2025五一数学建模竞赛B题】 矿山数据处理问题|建模过程+完整代码论文全解全析

你是否在寻找数学建模比赛的突破点&#xff1f;数学建模进阶思路&#xff01; 作为经验丰富的美赛O奖、国赛国一的数学建模团队&#xff0c;我们将为你带来本次数学建模竞赛的全面解析。这个解决方案包不仅包括完整的代码实现&#xff0c;还有详尽的建模过程和解析&#xff0c…