TOP100-回溯(二)

4.39. 组合总和

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

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

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

示例 1:

输入:candidates =[2,3,6,7],target =7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

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

示例 3:

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

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

思路:

本题没有数量要求,可以无限重复,但是有总和的限制,所以间接的也是有个数的限制。因为本题没有组合数量要求,仅仅是总和的限制,所以递归没有层数的限制,只要选取的元素总和超过target,就返回!

代码:

// 剪枝优化
class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(candidates); // 先进行排序backtracking(res, new ArrayList<>(), candidates, target, 0, 0);return res;}public void backtracking(List<List<Integer>> res, List<Integer> path, int[] candidates, int target, int sum, int idx) {// 找到了数字和为 target 的组合if (sum == target) {res.add(new ArrayList<>(path));return;}for (int i = idx; i < candidates.length; i++) {// 如果 sum + candidates[i] > target 就终止遍历if (sum + candidates[i] > target) break;path.add(candidates[i]);backtracking(res, path, candidates, target, sum + candidates[i], i);path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素}}
}

Python: 

class Solution:def backtracking(self, candidates, target, total, startIndex, path, result):if total == target:result.append(path[:])returnfor i in range(startIndex, len(candidates)):if total + candidates[i] > target:continuetotal += candidates[i]path.append(candidates[i])self.backtracking(candidates, target, total, i, path, result)total -= candidates[i]path.pop()def combinationSum(self, candidates, target):result = []candidates.sort()  # 需要排序self.backtracking(candidates, target, 0, 0, [], result)return result

5.22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

提示:

  • 1 <= n <= 8

思路:

回溯法,想象是否成功,通通建立2*n高的树,设置左右标志;加入左括号,则左标志+1,加入右括号,则右标志+1;仅当到达叶子节点时,左右标志也为n,才算做正确的输出。

代码:

class Solution:def generateParenthesis(self, n: int) -> List[str]:if n<=0:return []res = []def dfs(paths,left,right):if left>n or right >left: return#左括号多了,和右括号大于左括号数时,直接拜拜if len(paths) == n*2:res.append(paths)returndfs(paths+'(',left+1,right)dfs(paths+')',left,right+1)dfs('',0,0)return res

6.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 {public int m;public int n;public boolean exist(char[][] board, String word) {m=board.length;n=board[0].length;char[] words=word.toCharArray();for(int i =0;i<m;i++){for(int j = 0;j<n;j++){if(dfs(board,words,i,j,0))return true;}}return false;}boolean dfs(char[][] board,char[] word,int i,int j,int k){if(i>=m || i<0 || j>=n || j<0 || board[i][j] != word[k])return false;if(k == word.length-1)return true;board[i][j]='\0';boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);board[i][j] = word[k];return res;}
}

7.131. 分割回文串

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串。返回 s 所有可能的分割方案。

示例 1:

输入:s = "aab"
输出:[["a","a","b"],["aa","b"]]

示例 2:

输入:s = "a"
输出:[["a"]]

提示:

  • 1 <= s.length <= 16
  • s 仅由小写英文字母组成

思路:

1.先写一个函数用于判断是否为回文串
2.要划分出所有的子串,就是通过构建树来完成,而这个过程会用到深度优先搜索dfs(i)这个i是指第i个以后还没有遍历处理,而不是对第i个做操作,子串操作s.substring(start,end+1)
其他内容详见

代码:

class Solution {private String s;private final List<List<String>> res = new ArrayList<>();private final List<String> path = new ArrayList<>();//路径变量public List<List<String>> partition(String s) {this.s=s;//首先给字符串赋值//开始回溯(递归寻找)dfs(0,0);return res; }private boolean isHuiWen(int left, int right){while(left<right)if(s.charAt(left++)!=s.charAt(right--))return false;return true;}//i后面的还没处理,从Start开始private void dfs(int i, int  start){if(i==s.length()){res.add(new ArrayList<>(path));//说明这个已经完成了,复制return;}//不选i与i+1之间的逗号if(i < s.length() - 1)dfs(i+1,start);// 选 i 和 i+1 之间的逗号(把 s[i] 作为子串的最后一个字符)//要对于检查回文的部分做操作,也就是把s[i]作为字符串的最后一个字符,//需要判断从start到iif(isHuiWen(start,i)){path.add(s.substring(start,i+1));//切割子串,第i+1不算入//判断下一个字符作为头dfs(i+1,i+1);path.remove(path.size()-1);//恢复现场}}}

8.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

思路:

首先,抄来回溯的模板:

void backtracking(参数){if(终止条件){存结果;return;        }for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){处理节点;backtracking(路径,选择列表);//递归回溯,撤销处理结果;}}

 递归函数参数:

依然是定义全局变量二维数组result来记录最终的结果。

参数n是棋盘的大小,然后用row来记录当前遍历到了棋盘的第几层

这部分代码是:

result=[[]*n]*n
def backtrak(n , row , chessboard):#chessboard为棋盘

我们是逐行去寻找的,因此可以想到当当前遍历行数row为第n行时,回溯到了终点。因此回溯终点为:

if row == n :result.push_back(chessboard)return

单层搜索的逻辑:

递归深度其实就是row控制棋盘的行,每一层里for循环的col控制棋盘的列,一行一列决定了皇后的位置。
每次都是从新的一行开始搜索,因此都是从0开始

代码如下:

for(int col = 0; col < n ; col++){if(isValid(row,col,chessboard,n))chessboard[row][col] = 'Q';backtrack(n,row+1,chessboard);chessboard[row][col] = '.';    
}

验证是否合法的逻辑是:

1.不能同行
2.不能同列
3.不能同斜线(45度和135度)

代码:

bool isVaild(int row,int col, vector<string>& chessboard , int n){for(int i= 0;i < row ; i++){//同一列if(chessboard[i][col]=='Q')return false;}//45度检查for(int i= row-1 ,int j =col-1;i >=0 && j >=0 ; i--,j--){if(chessboard[i][j]=='Q')return false;    }//135度检查for(int i= row-1 ,int j =col+1;i >=0 && j < n  ; i--,j++){if(chessboard[i][j]=='Q')return false;  }return true;}

 总结来说:

棋盘的宽度就是for循环的长度,递归的深度就是棋盘的高度,这样就可以套进回溯法的模板里了

代码:

class Solution:def solveNQueens(self, n: int) -> List[List[str]]:result = []  # 存储最终结果的二维字符串数组chessboard = ['.' * n for _ in range(n)]  # 初始化棋盘self.backtracking(n, 0, chessboard, result)  # 回溯求解return [[''.join(row) for row in solution] for solution in result]  # 返回结果集def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None:if row == n:result.append(chessboard[:])  # 棋盘填满,将当前解加入结果集returnfor col in range(n):if self.isValid(row, col, chessboard):chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:]  # 放置皇后self.backtracking(n, row + 1, chessboard, result)  # 递归到下一行chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:]  # 回溯,撤销当前位置的皇后def isValid(self, row: int, col: int, chessboard: List[str]) -> bool:# 检查列for i in range(row):if chessboard[i][col] == 'Q':return False  # 当前列已经存在皇后,不合法# 检查 45 度角是否有皇后i, j = row - 1, col - 1while i >= 0 and j >= 0:if chessboard[i][j] == 'Q':return False  # 左上方向已经存在皇后,不合法i -= 1j -= 1# 检查 135 度角是否有皇后i, j = row - 1, col + 1while i >= 0 and j < len(chessboard):if chessboard[i][j] == 'Q':return False  # 右上方向已经存在皇后,不合法i -= 1j += 1return True  # 当前位置合法

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

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

相关文章

Java设计模式—备忘录模式(快照模式)

定义 备忘录模式提供了一种状态恢复的实现机制&#xff0c;使得用户可以方便地回到一个特定的历史步骤&#xff0c;当新的状态无效或者存在问题时&#xff0c;可以使用暂时存储起来的备忘录将状态复原&#xff0c;很多软件都提供了撤销&#xff08;Undo&#xff09;操作&#…

类模板成员函数的类外实现

能够掌握类模板中的成员函数的类外实现&#xff1a; #include <iostream> #include <Windows.h> #include <string>using namespace std;template<typename T1, typename T2> class Person { public:T1 m_Name;T2 m_Age;Person(T1 name, T2 age);void…

Ubuntu 系统下安装 Nginx

目录 一、Nginx是什么 ​二、Ubuntu 系统下安装 Nginx 1、安装包下载 2、上传服务器并解压缩 3、依赖配置安装 4、生成编译脚本 ​5、编译 6、开始安装 7、设置为随机自启动 7.1、创建 nginx.service 文件&#xff0c;将以下内容粘贴到文件中 7.2、将 nginx.service…

基于springboot酒店管理平台

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于酒店管理平台系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了酒店管理平台系统&#xff0c;它彻底改变了过…

FPGA工程师职业发展道路

作为FPGA工程师&#xff0c;你可以通过以下几个步骤来发展自己的职业道路&#xff1a; 1. 学习基础知识&#xff1a;首先&#xff0c;你需要学习数字电路设计和计算机体系结构的基础知识。了解FPGA的原理、架构和工作原理是非常重要的。 2. 掌握HDL编程语言&#xff1a;掌握至…

EfficientSAM 项目排坑

EfficientSAM 项目排坑 任务过程记录创建环境运行示例 任务 跑通这个项目代码 过程记录 创建环境 readme里没有说具体怎么配置环境&#xff0c;所以可能对我来说还挺困难的。 现把项目git下来&#xff1a; git clone https://github.com/yformer/EfficientSAM.git cd Effi…

熊猫左右转-第15届蓝桥第5次STEMA测评Scratch真题精选

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第174讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

|行业洞察·汽车|《2024新能源汽车行业及营销趋势报告-20页》

报告的主要内容解读&#xff1a; 新能源汽车行业概述及品牌分布&#xff1a; 近年来&#xff0c;中国新能源汽车销量增速高&#xff0c;市场占有率快速提升&#xff0c;成为汽车行业的重要增量。新能源汽车消费者趋向年轻化、女性化和高端化&#xff0c;对高科技、新体验有较高…

深入探讨多线程编程:从0-1为您解释多线程(下)

文章目录 6. 死锁6.1 死锁原因 6.2 避免死锁的方法加锁顺序一致性。超时机制。死锁检测和解除机制。 6. 死锁 6.1 死锁 原因 系统资源的竞争&#xff1a;&#xff08;产生环路&#xff09;当系统中供多个进程共享的资源数量不足以满足进程的需要时&#xff0c;会引起进程对2…

基于Axios封装请求---防止接口重复请求解决方案

一、引言 前端接口防止重复请求的实现方案主要基于以下几个原因&#xff1a; 用户体验&#xff1a;重复发送请求可能导致页面长时间无响应或加载缓慢&#xff0c;从而影响用户的体验。特别是在网络不稳定或请求处理时间较长的情况下&#xff0c;这个问题尤为突出。 服务器压力…

V R虚拟现实元宇宙的前景|虚拟现实体验店加 盟合作|V R设备在线购买

VR&#xff08;虚拟现实&#xff09;技术作为一种新兴的技术&#xff0c;正在逐渐改变人们的生活和工作方式。随着技术的不断进步&#xff0c;人们对于元宇宙的概念也越来越感兴趣。元宇宙是一个虚拟世界&#xff0c;通过VR技术可以实现人们在其中进行各种活动和交互。 元宇宙的…

【C++程序员的自我修炼】基础语法篇(一)

心中若有桃花源 何处不是水云间 目录 命名空间 &#x1f49e;命名空间的定义 &#x1f49e; 命名空间的使用 输入输出流 缺省参数 函数的引用 引用的定义&#x1f49e; 引用的表示&#x1f49e; 引用的特性&#x1f49e; 常量引用&#x1f49e; 引用的使用场景 做参数 做返回值…