【LeetCode HOT 100】详细题解之回溯篇

【LeetCode HOT 100】详细题解之回溯篇

  • 回溯法的理论基础
    • 回溯法解决的问题
    • 理解回溯法
    • 回溯法模板
  • 46 全排列
    • 思路
    • 代码
  • 78 子集
    • 思路
    • 代码
  • 17 电话号码的字母组合
    • 思路
    • 代码
  • 39 组合总和
    • 思路
    • 代码
  • 22 括号生成
    • 思路
    • 代码
  • 79 单词搜索
    • 思路
    • 代码
  • 131 分割回文串
    • 思路
    • 代码
  • 51 N皇后
    • 思路
    • 代码

回溯法的理论基础

这里参考代码随想录中的回溯章节。代码随想录 (programmercarl.com)

回溯法是一种搜索的方式,是穷举所有的可能选出我们想要的答案。但是由于回溯常常和递归结合在一起,所以回溯法会比较难以理解。

回溯法解决的问题

使用回溯算法求解的一般有组合,分割,子集,排列,棋盘等问题。

组合问题:N个数里按照一定规则找出k个数的集合

切割问题:一个字符串按照规则存在几种切割方式

子集问题:N个数的集合里有多少符合条件的子集

排列问题:N个数按一定规则全排列,存在几种排列方式

棋盘问题:N皇后,解数独等等

注意排列和组合的区别,组合不强调元素顺序,排列强调元素顺序。举个例子

{1,2},{2,1}为同一个组合,但是为两个不同的排列

理解回溯法

回溯法解决的问题可以抽象为树形结构。因为解决的都是在集合中递归查找子集,集合的大小为树的宽度递归的深度构成树的深度

回溯法模板

  • 回溯函数模板返回值以及参数

在回溯算法中,函数起名字为backtracking,这个起名随意。

回溯算法中函数返回值一般为void。

参数,因为回溯算法需要的参数可不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。

void backtracking(参数)
  • 回溯算法终止条件

既然是树形结构,遍历树形结构一定要有终止条件。

所以回溯也有要终止条件。

什么时候达到了终止条件,树中就可以看出,一般来说搜到叶子节点了,也就找到了满足条件的一条答案,把这个答案存放起来,并结束本层递归。

所以回溯函数终止条件伪代码如下:

if (终止条件) {存放结果;return;
}
  • 回溯搜索的遍历过程

在上面我们提到了,回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。

在这里插入图片描述

回溯函数遍历过程伪代码如下:

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

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就执行多少次。

backtracking这里自己调用自己,实现递归。

大家可以从图中看出for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。

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

46 全排列

46. 全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

思路

先模拟一下全排列的搜索过程。从根节点搜索到叶子节点为递归,纵向遍历的过程。

在这里插入图片描述

1.回溯参数:在全排列问题中,我们需要一个标记数组来对当前数字是否使用过进行标记,因此需要传入的参数包括一个used数组。

2.终止条件:当递归搜索到叶子节点时,说明找到一个符合要求的全排列,可以终止并将当前排列假如结果集中。

3.单层搜索逻辑:如果当前数字未被使用过(used[i]=false),将used[i]置为true,之后继续搜索。如果使用过则跳过当前数字。

代码

最终代码如下。

注意终止条件这里

    //终止条件:递归搜索到叶子节点if(path.size()==nums.length){res.add(new ArrayList(path)); return; //注意这里要return,因为只有遍历到叶子节点时才会取结果}

需要res.add(new ArrayList(path)); 而不是直接res.add(path)

res.add(new ArrayList(path))是添加path的一个副本进入结果集。

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> permute(int[] nums) {/**用used数组来去重,不能用startIndex来控制不重复*/boolean[] used = new boolean[nums.length];backtracking(nums,used);return res;}private void backtracking(int[] nums,boolean[] used){//终止条件:递归搜索到叶子节点if(path.size()==nums.length){res.add(new ArrayList(path)); return; //注意这里要return,因为只有遍历到叶子节点时才会取结果}//3.单层搜索逻辑for(int i = 0;i<nums.length;i++){if(used[i]){  //用过的话,跳过当前数字。continue;}used[i]=true;path.add(nums[i]);backtracking(nums,used); //递归path.remove(path.size()-1);used[i] = false;}}}

78 子集

78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的

子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

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

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

思路

和全排列问题不同,全排列问题是收集树的叶子节点。而子集问题是找树的所有节点。本题中,无序并且取过的元素不会重复取,因此回溯遍历的时候,下一层需要从startIndex开始遍历(不能取之前取过的元素。)

在这里插入图片描述

​ 1.回溯传参:startIndex为开始搜索的下标,通过startIndex来记录本层递归中,集合从哪里遍历

​ 2.终止条件: startIndex>=nums.length的时候,return (注意要在终止条件前收集子集,因为每进入新一层的递归,都需要收集子集)

​ 3.单层处理逻辑

​ path.add(i)

    private void backtracking(int[] nums,int startIndex){//!!收集子集,每到达递归的新一层,就会生成新的子集。所以要在终止条件之前收集子集res.add(new ArrayList(path));if(startIndex>nums.length){return;}for(int i = startIndex;i<nums.length;i++){path.add(nums[i]); //子集收集元素backtracking(nums,i+1); //从i+1开始,元素不重复取path.remove(path.size()-1); //回溯}}

代码

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> subsets(int[] nums) {/**本题和之前的组合问题相似,由于数组中元素互不相同,所以不用判断相同的情况1.回溯传参:startIndex为开始搜索的下标,通过startIndex来记录本层递归中,集合从哪里遍历2.终止条件startIndex>=nums.length的时候,return3.单层处理逻辑path.add(i)*/backtracking(nums,0);return res;}private void backtracking(int[] nums,int startIndex){//!!收集子集,每到达递归的新一层,就会生成新的子集。所以要在终止条件之前收集子集res.add(new ArrayList(path));if(startIndex>nums.length){return;}for(int i = startIndex;i<nums.length;i++){path.add(nums[i]); //子集收集元素backtracking(nums,i+1); //从i+1开始,元素不重复取path.remove(path.size()-1); //回溯}}
}

17 电话号码的字母组合

17. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

img

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。

思路

算法思路

使用回溯算法生成所有可能的字母组合。回溯算法是一种通过试错来找到所有解决方案的算法。在这个问题中,我们需要生成所有可能的字母组合。

算法步骤

  1. 初始化:定义一个字符串数组 num2String 来映射数字到对应的字母集合。
  2. 递归终止条件:如果 path 的长度等于 digits 的长度,说明已经找到了一个完整的组合,将其添加到结果列表中。
  3. 单层递归逻辑:使用 StringBuilder 来动态构建字符串,因为 String 是不可变的,每次修改都需要创建一个新的字符串对象。
  4. 回溯:在递归调用后,使用 path.deleteCharAt() 删除最后一个字符,以便尝试下一个可能的字母。

代码

class Solution {List<String> res = new ArrayList<>();StringBuilder path = new StringBuilder();String[] num2String = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};public List<String> letterCombinations(String digits) {/**回溯回溯前需要定义数字->字符集的映射暴力搜索所有可能的组合1.递归终止条件:path.length() == digits.length()时,说明每个数字对应的字母都选择了一个2.单层递归逻辑注意,在这里由于需要不停的使用字符串拼接操作,所以用StringBuilder来实现path.append()path.deleteCharAt()3.注意String的用法String中获取某个位置的元素只能用charAt,数组才可以用下标获取。*/if(digits.length()==0||digits==null){return res;}backtracking(digits,0);return res;}private void backtracking(String digits,int index){ //index为当前digits中下标为index指向的数字if( index == digits.length()){ //遍历到digits结尾res.add(path.toString());return;}//index = 1,digits = "23" ,那么cur_num = digits[1] = 3int cur_num = digits.charAt(index)-'0';//遍历当前数组对应的字符集,比如当前数字为3,对应的字符集为“def”for(int i = 0;i<num2String[cur_num].length();i++){path.append(num2String[cur_num].charAt(i));backtracking(digits,index+1);path.deleteCharAt(path.length()-1);}}
}

39 组合总和

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

思路

算法思路

无重复元素的整数数组candidates,但是其中的同一个数字可以被无限制重复选取。因此,本题仍然需要startIndex来横向控制往后的遍历。但是纵向就不需要向前,因为仍然可以使用当前指向的元素。backtracking(candidates,target,i); //注意!因为可重复选,所以这里递归传进的是i而非i+1

    for(int i = startIndex;i<candidates.length;i++){//2.单层搜索逻辑target -=candidates[i];path.add(candidates[i]);backtracking(candidates,target,i); //注意!因为可重复选,所以这里递归传进的是i而非i+1path.remove(path.size()-1);target += candidates[i];}

1.递归函数参数:使用两个全局变量。二维数组res存放结果集,数组path存放符合条件的结果。题目给出的参数,集合candidates和目标值target,每次用target减去当前的数字,当target==0说明找到结果。以及startIndex控制for循环的起始位置。

2.递归终止条件:当target<0时,再搜索下去没有意义,target=0时,需要收集结果。

3.单层搜索逻辑:从startIndex开始,搜索candidates集合。

在这里插入图片描述

代码

class Solution {List<List<Integer>> res = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {/**本题和77组合总和的区别在于本题中的元素可重复选取,并且没有对组合元素数量进行限制通过组合的和来限制树的深度for控制的是横向遍历,递归控制的是纵向遍历举个例子[2,5,3]在这里,可重复选说明递归的话,第一次在[2,5,3]中选,递归时仍然在[2,5,3]中选。那如何控制元素的往前遍历呢,通过for循环控制。*/backtracking(candidates,target,0);return res;}private void backtracking(int[] candidates,int target,int startIndex){//1.返回结果if(target<0){return;}if(target==0){res.add(new ArrayList(path));return;}for(int i = startIndex;i<candidates.length;i++){//2.单层搜索逻辑target -=candidates[i];path.add(candidates[i]);backtracking(candidates,target,i); //注意!因为可重复选,所以这里递归传进的是i而非i+1path.remove(path.size()-1);target += candidates[i];}}
}

22 括号生成

22. 括号生成

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= n <= 8

思路

  • 回溯,通过记录左右括号的数量判断当前应该尝试放左括号还是右括号
  • 回溯参数:左括号数量,右括号数量
  • 终止条件:左括号数量为0且右括号数量为0,说明放完了
  • 单层回溯逻辑:如果左括号数量大于0,放左括号,之后同样的逻辑放右括号

代码

class Solution {List<String> res = new ArrayList<>();StringBuilder path = new StringBuilder();public List<String> generateParenthesis(int n) {/**回溯,通过记录左右括号的数量判断当前应该尝试放左括号还是右括号回溯参数:左括号数量,右括号数量终止条件:左括号数量为0且右括号数量为0,说明放完了单层回溯逻辑:如果左括号数量大于0,放左括号,之后同样的逻辑放右括号*/backtracking(n,n);return res;}private void backtracking(int leftCount,int rightCount){if(leftCount == 0 && rightCount==0){res.add(path.toString());return;}//假如leftCount >rightCount,说明剩余的左括号数量大于右括号//只有剩余左括号数量<=右括号数量,才有可能组成合法的括号// (( ) 此时剩余左括号为1,右括号为2,有可能组成合法的括号// (())) 此时剩余左括号为1,右括号为0,不可能组成合法的括号if((leftCount != 0 || rightCount != 0) && leftCount <= rightCount){if(leftCount!=0){path.append('(');leftCount--;backtracking(leftCount,rightCount);leftCount++;path.deleteCharAt(path.length()-1);}if(rightCount!=0){path.append(')');rightCount--;backtracking(leftCount,rightCount);rightCount++;path.deleteCharAt(path.length()-1);}}}}

79 单词搜索

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
  • boardword 仅由大小写英文字母组成

**进阶:**你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?

思路

​ 深搜
​ 以board中的每个位置为起点,向上下左右四个方向进行深度搜索
​ dfs参数:除了board,word,还包括当前匹配到的word中的index,以及搜索方向x,y
​ 返回条件:x,y溢出边界,board[x] [y]!=word[index],board[x] [y]=‘.‘说明访问过
​ index = word.length()-1说明匹配成功
​ 单层搜索逻辑:匹配上了后给当前位置做个访问过的标记,即将当前位置元素置’.’,回溯完后再修改为原来的值

代码

class Solution {public boolean exist(char[][] board, String word) {/**回溯以board中的每个位置为起点,向上下左右四个方向进行深度搜索回溯参数:除了board,word,还包括当前匹配到的word中的index,以及搜索方向x,y返回条件:x,y溢出边界,board[x][y]!=word[index],board[x][y]='.'说明访问过index = word.length()-1说明匹配成功单层搜索逻辑:匹配上了后给当前位置做个访问过的标记,即将当前位置元素置'.',回溯完后再修改为原来的值*/for(int i = 0;i<board.length;i++){for(int j = 0;j<board[0].length;j++){if(dfs(board,word,0,i,j)){return true;}}}return false;}private boolean dfs(char[][] board,String word,int index,int x,int y){//1.x,y溢出边界,当前元素不等于word中的字母,访问过if(x<0 || x>board.length-1 || y<0 || y>board[0].length-1 || word.charAt(index)!=board[x][y] || board[x][y]=='.' ){return false;}if(index == word.length()-1){return true;}char temp = board[x][y];board[x][y] = '.';boolean res = dfs(board,word,index+1,x-1,y) || dfs(board,word,index+1,x+1,y)||dfs(board,word,index+1,x,y-1) || dfs(board,word,index+1,x,y+1);board[x][y] = temp;return res;}
}

131 分割回文串

131. 分割回文串

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

示例 1:

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

示例 2:

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

提示:

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

思路

​ 分割问题也是用回溯来解决,具体如何回溯呢
​ 以aab为例,是要模拟切割线
​ aab 开始切割
​ 第一层 a|ab aa|b aab|
​ 第二层 a|a|b aa|b| 终止
​ 第三层 a|a|b| 终止
​ 可以看到startIndex此时为切割线在字符串中的位置
​ 1.回溯的参数:结果集,路径集,切割的位置
​ 2.回溯终止条件
​ 当切割线移动到String的末尾时,切割停止
​ 3.单层处理逻辑
​ 截取当且子串[startIndex,i],如果当前子串是回文串,则将结果加入到路径中,递归(i+1)

回溯搜索图如下。

在这里插入图片描述

代码

class Solution {List<List<String>> res = new ArrayList<>();List<String> path = new ArrayList<>();public List<List<String>> partition(String s) {backtracking(s,0);return res;}private void backtracking(String s,int startIndex){//因为从起始位置一个个加的,所以结束时start一定=s.lengthif(startIndex==s.length()){//这里要创建path的copy版本res.add(new ArrayList(path));return;}for(int i = startIndex;i<s.length();i++){String substr = s.substring(startIndex,i+1);//截取子串[startIndex,i]if(check(substr)){path.add(substr);backtracking(s,i+1); //注意这里[startIndex,i]之间的已经被截取了,下一步要截取的为i+1开始的path.remove(path.size()-1);}}}//检查是否回文private boolean check(String s){for(int i = 0;i<s.length()/2;i++){if(s.charAt(i)!=s.charAt(s.length()-1-i)){return false;}}return true;}}

51 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

思路

本题中的回溯搜索为:for循环来搜索棋盘的每一列,递归来搜索棋盘的每一行。

  • 回溯函数参数:row:当前搜索到第row行,board[] []为当前棋盘,n为棋盘总行数。

  • 递归终止条件:当搜索到第n行时返回。

  • 单层搜索逻辑:每一层的列都要从新一行的起始位置开始搜索,所以for循环从0开始。

  • 判断合法位置

        //1.要在[row,col]位置填入Q//1.判断第row行其他位置是否存在Q// 在for循环中,每一行只会同时放一个Q,所以不用判断row行的其他位置是否存在Q//2.判断第col列其他位置是否存在Q,注意,由于此时row,n之间的行还没有遍历,所以不用考虑//3.判断斜对角线位置是否存在Q,注意,由于row+1还没有遍历到,所以只需要遍历row-1的情况//4.判断斜对角线位置是否存在Q
    
        //合法位置的判断private boolean isValid(char[][] board,int row,int col){int n = board[0].length;//1.要在[row,col]位置填入Q//1.判断第row行其他位置是否存在Q// 在for循环中,每一行只会同时放一个Q,所以不用判断row行的其他位置是否存在Q//2.判断第col列其他位置是否存在Q,注意,由于此时row,n之间的行还没有遍历,所以不用考虑for(int i = 0;i<row;i++){if(board[i][col]=='Q'){return false;}}//3.判断斜对角线位置是否存在Q,注意,由于row+1还没有遍历到,所以只需要遍历row-1的情况for(int i = row-1,j = col-1;i>=0&&j>=0;i--,j--){if(board[i][j]=='Q' && i!=row && j != col){return false;}}//4.判断斜对角线位置是否存在Qfor(int i = row-1,j = col+1;i>=0&&j<n;i--,j++){if(board[i][j]=='Q' && i!=row && j != col){return false;}}return true;}
    

回溯搜索过程如下。

在这里插入图片描述

代码

class Solution {List<List<String>> res = new ArrayList<>();public List<List<String>> solveNQueens(int n) {/**回溯参数:n一共有n行,row:当前搜索到第row行,board[][]为棋盘返回:当搜索到第n行时返回*/char[][] board = new char[n][n];for(char[] c: board){Arrays.fill(c,'.');}    backtracking(n,0,board);return res;}private void backtracking(int n,int row,char[][] board){if(row==n){List<String> tmp = Array2List(board);res.add(tmp);return;}for(int col = 0;col<n;col++){if(isValid(board,row,col)){board[row][col] = 'Q';backtracking(n,row+1,board);board[row][col] = '.';}}}//将得到的char[][]数组转换为List<String>private List<String> Array2List(char[][] board){List<String> temp = new ArrayList<>();for(char[] row:board){temp.add(new String(row));}return temp;}//合法位置的判断private boolean isValid(char[][] board,int row,int col){int n = board[0].length;//1.要在[row,col]位置填入Q//1.判断第row行其他位置是否存在Q// 在for循环中,每一行只会同时放一个Q,所以不用判断row行的其他位置是否存在Q//2.判断第col列其他位置是否存在Q,注意,由于此时row,n之间的行还没有遍历,所以不用考虑for(int i = 0;i<row;i++){if(board[i][col]=='Q'){return false;}}//3.判断斜对角线位置是否存在Q,注意,由于row+1还没有遍历到,所以只需要遍历row-1的情况for(int i = row-1,j = col-1;i>=0&&j>=0;i--,j--){if(board[i][j]=='Q' && i!=row && j != col){return false;}}//4.判断斜对角线位置是否存在Qfor(int i = row-1,j = col+1;i>=0&&j<n;i--,j++){if(board[i][j]=='Q' && i!=row && j != col){return false;}}return true;}
}

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

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

相关文章

打造梦幻AI开发环境:一步步解锁高效配置的魅力

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

疾病防控|基于springBoot的疾病防控综合系统设计与实现(附项目源码+论文+数据库)

私信或留言即免费送开题报告和任务书&#xff08;可指定任意题目&#xff09; 目录 一、摘要 二、相关技术 三、系统设计 四、数据库设计 五、核心代码 六、论文参考 七、源码获取 一、摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何…

基于SpringBoot+Vue的农场管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

通过Fiddler抓包采集某音作品列表,视频列表

声明&#xff1a;文章仅用于学习交流,如有侵权请联系删除 今天分享下某音app作品列表采集方法&#xff0c;我只详细说一下大步骤&#xff0c;细节就不多说了&#xff0c;留着大家去试吧 我们通过Fiddler 快捷方式 配置好代理 打开抖音进行抓包&#xff0c;随便找个达人打开主…

计算机的错误计算(一百一十七)

摘要 算式“(5^25*(1/25)^(1/5)*3^25(1/25)^(1/5)*5^25*3^(251/5)-(9/25)^(1/5)*3^25*5^25-(1/25)^(1/5)*3^25*5.0^25*(13^(1/5)-3^(2/5.0)))” 的准确值是0. 但是&#xff0c;Python 与 Excel 均输出了错误结果&#xff1a;一个含有15位整数&#xff0c;一个含有14位整数。 …

stm32学习笔记-RTC实时时钟

文章目录 一、RTC基础知识1.1 RTC简介1.2 RTC的晶振 二、stm32的RTC2.1 RTC和后备寄存器2.2 stm32 RTC结构框图及特性 三、stm32 RTC编程2.1 RTC初始化2.2 RTC控制程序 一、RTC基础知识 1.1 RTC简介 实时时钟的缩写是RTC(Real_Time Clock)。RTC 是集成电路&#xff0c;通常称…

【机器学习】深度学习、强化学习和深度强化学习?

深度学习、强化学习和深度强化学习是机器学习的三个重要子领域。它们有着各自独特的应用场景和研究目标&#xff0c;虽然都属于机器学习的范畴&#xff0c;但各自的实现方式和侧重点有所不同。 1. 深度学习&#xff08;Deep Learning&#xff09; 深度学习是一种基于神经网络的…

76.【C语言】perror函数介绍

1.cplusplus的官网介绍 cplusplus的介绍 点我跳转 2.翻译 函数 perror void perror ( const char * str ); 打印错误信息 将errno(最后一个错误数字)的值解释为错误信息,之后把它打印到stderr中(标准错误输出流,通常是控制台)(备注有关"流"的概念在75.【C语言】文件…

CMake 属性之目录属性

【写在前面】 CMake 的目录属性是指在特定目录&#xff08;及其子目录&#xff09;范围内有效的设置。 这些属性不同于全局变量或目标&#xff08;Target&#xff09;属性&#xff0c;它们提供了一种机制&#xff0c;允许开发者为项目中的不同部分定义不同的构建行为。 通过目录…

Jax(Random、Numpy)常用函数

目录 Jax vmap Array reshape Random PRNGKey uniform normal split choice Numpy expand_dims linspace jax.numpy.linalg[pkg] dot matmul arange interp tile reshape Jax jit jax.jit(fun, in_shardingsUnspecifiedValue, out_shardingsUnspecifiedVa…

docker compose一键部署容器监控 CAdvisor+InfluxDB+Granfana

docker compose一键部署容器监控 CAdvisorInfluxDBGranfana CAdvisor监控收集InfluxDB存储数据Granfana展示图表 1、原生命令 通过docker stats 命令可以查看当前宿主机上所有创建的容器的CPU,内存和网络流量等信息 docker stats 缺点&#xff1a;只能查看当前宿主机的全部…

Pyppeteer:如何在 Python 中使用 Puppeteer 和 Browserless?

Python 中的 Pyppeteer 是什么&#xff1f; Pyppeteer 是流行的 Node.js 库 Puppeteer 的 Python 移植版本&#xff0c;用于以编程方式控制无头 Chrome 或 Chromium 浏览器。 本质上&#xff0c;Pyppeteer 允许 Python 开发人员在 Web 浏览器中自动执行任务&#xff0c;例如抓…

多选框的单选操作 Element ui

文章目录 样式预览Q&#xff1a;为什么要这么做&#xff1f;实现原理探索路程 样式预览 Q&#xff1a;为什么要这么做&#xff1f; 单选框的样式不够好看单选框因为框架等原因&#xff0c;无法取消选择 实现原理 判断多选框绑定的 value&#xff0c;如果长度为2&#xff0c;那…

oracle-函数-instr()的妙用以及相似功能like

INSTR(C1,C2[,I[,J]]) 【功能】在一个字符串中搜索指定的字符,返回发现指定的字符的位置; 【说明】多字节符(汉字、全角符等)&#xff0c;按1个字符计算 【参数】 C1 被搜索的字符串 C2 希望搜索的字符串 I 搜索的开始位置,默认为1 J 第J次出现的位置,默认为1 【…

RTSP RTP RTCP SDP基础知识

理论 流&#xff08;Streaming &#xff09; 是近年在 Internet 上出现的新概念&#xff0c;其定义非常广泛&#xff0c;主要是指通过网络传输多媒体数据的技术总称。 流式传输分为两种 顺序流式传输 (Progressive Streaming) 实时流式传输 (Real time Streaming) ​​​​​…

国产长芯微LDC5422单通道、16位、电流源和电压输出DAC,HART连接完全P2P替代AD5422

描述 LDC5422是低成本、精密、完全集成、16位数模转换器(DAC)&#xff0c;内置可编程电流源和可编程电压输出&#xff0c;设计用于满足工业过程控制应用的需要。 输出电流范围可编程设置为4 mA至20 mA、0 mA至20 mA或者超量程的0 mA至24 mA。 此产品的LFCSP版本有一个CAP2引脚…

胤娲科技:00后揭秘——AI大模型的可靠性迷局

当智能不再“靠谱”&#xff0c;我们该何去何从&#xff1f; 想象一下&#xff0c;你向最新的GPT模型提问&#xff1a;“9.9和9.11哪个大&#xff1f;”这本应是个小菜一碟的问题&#xff0c;却足以让不少高科技的“大脑”陷入沉思&#xff0c; 甚至给出令人啼笑皆非的答案。近…

vite学习教程06、vite.config.js配置

前言 博主介绍&#xff1a;✌目前全网粉丝3W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&#xff1a;Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。 博主所有博客文件…

智能桥梁:Profinet至CanOpen,台达伺服无缝对接

在工业自动化领域&#xff0c;将西门子S7-1200系列PLC与具备CANOPEN通讯功能的伺服驱动器设备集成时&#xff0c;由于PLC默认采用PROFINET实时以太网通讯协议&#xff0c;直接连接存在协议不匹配问题。为解决这一挑战&#xff0c;开疆推出的Profinet转CANOPEN网关提供了高效便捷…

Java基础知识——String篇

一、String 1、是什么 String 是 Java 中用于表示字符串的类。Java 中的字符串是不可变的&#xff0c;也就是说一旦创建&#xff0c;字符串的内容无法更改。 2、如何构造 &#xff08;1&#xff09;无参数构造方法&#xff1a; String str new String(); //创建一个空字符…