总结

技巧:

  • 子集和组合问题经常需要一个start来标记开始选择的起始位置来达到去重的目的,排列问题不用 【46.全排列】

  • 排列问题常需要一个used数组来标记已经选择的元素(也可以同时用于去重)

C++ 总结了回溯问题类型 带你搞懂回溯算法(大量例题)

  • ①画出递归树,找到状态变量(回溯函数的参数),这一步非常重要※
  • ②根据题意,确立结束条件
  • ③找准选择列表(与函数参数相关),与第一步紧密关联※
  • ④判断是否需要剪枝
  • ⑤作出选择,递归调用,进入下一层
  • ⑥撤销选择

【参考:回溯算法解题套路框架 :: labuladong的算法小抄】

46、51题

  • 模版
result = []
def backtrack(路径, 选择列表):if 满足结束条件:result.add(路径)returnfor 选择 in 选择列表:做选择backtrack(路径, 选择列表)撤销选择for 选择 in 选择列表:# 做选择将该选择再加入选择列表路径.add(选择)backtrack(路径, 选择列表)# 撤销选择路径.remove(选择)将该选择从选择列表移除

  • 切割问题类似于组合问题(131题、93题)
  • 子集和组合问题一般for循环从start开始
  • 排列问题一般for循环从0开始
    • 比如{1,2}求排列:{1},{2},{1,2},{2,1}
  • 去重先得对集合排序

C++ 总结了回溯问题类型 带你搞懂回溯算法(排列篇)

  • 【参考:回溯算法团灭排列/组合/子集问题_labuladong_微信公众号】

【参考:回溯算法入门级详解 + 练习(持续更新) - 全排列 - 力扣(LeetCode)】 巨详细

去重——用set去重
【参考:代码随想录# 回溯算法去重问题的另一种写法】

中等

78.子集

【参考:78. 子集 - 力扣(LeetCode)】

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> subsets(int[] nums) {List<Integer> track=new ArrayList<>(); //走过的路径backtarck(nums,0,track);return res;}public void backtarck(int[] nums,int start,List<Integer> track){if (start > nums.length) {return;}res.add(new ArrayList<>(track));for(int i=start;i<nums.length;i++){// 做选择(添加这个数)track.add(nums[i]);// 递归回溯(从i+1开始)backtarck(nums,i+1,track);// 撤销选择track.remove(track.size() - 1);}}
}

90. 子集 II

【参考:90. 子集 II - 力扣(LeetCode)】

需要去重

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> subsetsWithDup(int[] nums) {List<Integer> track=new ArrayList<>();int[] used=new int[nums.length];Arrays.sort(nums);backtarck(nums,0,used,track);return res;}public void backtarck(int[] nums,int start,int[] used,List<Integer> track){if (start > nums.length) {return;}res.add(new ArrayList<>(track));for(int i=start;i<nums.length;i++){// 去重if(i>0 && nums[i]==nums[i-1] && used[i-1]==0){continue;}// 做选择(添加这个数)track.add(nums[i]);used[i]=1;// 递归回溯(从i+1开始)backtarck(nums,i+1,used,track);// 撤销选择used[i]=0;track.remove(track.size() - 1);}}
}

77. 组合 ***

【参考:77. 组合 - 力扣(LeetCode)】

【参考:代码随想录# 第77题. 组合】

【参考:回溯算法 + 剪枝(Java) - 组合 - 力扣(LeetCode)】 这个写的巨详细

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> combine(int n, int k) {if(k<=0 || n<=0) return res;List<Integer> track=new ArrayList<>(); //走过的路径backtarck(n,k,1,track);// start从1开始return res;}// 范围 [1, n] 中所有可能的 k 个数的组合,start:从几开始public void backtarck(int n,int k,int start,List<Integer> track){if(k==track.size()){res.add(new ArrayList<>(track));return;}for(int i=start;i<=n;i++){// 做选择(添加这个数)track.add(i);System.out.println("递归之前 => " + track);// 递归回溯(从i+1开始)backtarck(n,k,i+1,track);// 撤销选择track.remove(track.size() - 1);System.out.println("递归之后 => " + track);}}public static void main(String[] args) {Solution solution = new Solution();int n = 5;int k = 3;List<List<Integer>> res = solution.combine(n, k);System.out.println(res);}
}递归之前 => [1]
递归之前 => [1, 2]
递归之前 => [1, 2, 3]
递归之后 => [1, 2]
递归之前 => [1, 2, 4]
递归之后 => [1, 2]
递归之前 => [1, 2, 5]
递归之后 => [1, 2]
递归之后 => [1]
递归之前 => [1, 3]
递归之前 => [1, 3, 4]
递归之后 => [1, 3]
递归之前 => [1, 3, 5]
递归之后 => [1, 3]
递归之后 => [1]
递归之前 => [1, 4]
递归之前 => [1, 4, 5]
递归之后 => [1, 4]
递归之后 => [1]
递归之前 => [1, 5]
递归之后 => [1]
递归之后 => []
递归之前 => [2]
递归之前 => [2, 3]
递归之前 => [2, 3, 4]
递归之后 => [2, 3]
递归之前 => [2, 3, 5]
递归之后 => [2, 3]
递归之后 => [2]
递归之前 => [2, 4]
递归之前 => [2, 4, 5]
递归之后 => [2, 4]
递归之后 => [2]
递归之前 => [2, 5]
递归之后 => [2]
递归之后 => []
递归之前 => [3]
递归之前 => [3, 4]
递归之前 => [3, 4, 5]
递归之后 => [3, 4]
递归之后 => [3]
递归之前 => [3, 5]
递归之后 => [3]
递归之后 => []
递归之前 => [4]
递归之前 => [4, 5]
递归之后 => [4]
递归之后 => []
递归之前 => [5]
递归之后 => []
[[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]作者:liweiwei1419
链接:https://leetcode-cn.com/problems/combinations/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-ma-/

剪枝优化

【参考:代码随想录# 第77题. 组合-剪枝优化】

  • 已经选择的元素个数:path.size();
  • 还需要的元素个数为: k - path.size();
  • 在集合n中起始位置i最大为 : n - (k - path.size()) + 1

为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

举个例子,n = 4,k = 2, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 2 - 0) + 1 = 3。i最大从3开始搜索 (组合[3, 4])。也就是说 i∈[1,3]i\in[1,3]i∈[1,3]

这里大家想不懂的话,建议也举一个例子,就知道是不是要+1了。

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

216. 组合总和 III

【参考:216. 组合总和 III - 力扣(LeetCode)】

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> combinationSum3(int k, int n) {List<Integer> track=new ArrayList<>(); //走过的路径backtarck(k,n,1,0,track);return res;}// sum 为已经收集的元素之和public void backtarck(int k, int n,int start,int sum,List<Integer> track){if(sum==n&&track.size()==k){res.add(new ArrayList<>(track));return;}               for(int i=start;i<10;i++){// 做选择(添加这个数)track.add(i);sum += i;// 递归回溯backtarck(k,n,i+1,sum,track); // 注意下一步start=i+1// 撤销选择sum -= i;track.remove(track.size() - 1);}}
}

39. 组合总和

【参考:39. 组合总和 - 力扣(LeetCode)】

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> combinationSum(int[] candidates, int target) {List<Integer> track=new ArrayList<>(); //走过的路径backtarck(candidates,target,0,0,track);return res;}public void backtarck(int[] candidates,int target,int sum,int start,List<Integer> track){if (sum > target) {return;}if(sum==target){res.add(new ArrayList<>(track));return;}for(int i=start;i<candidates.length;i++){// 做选择track.add(candidates[i]);sum+=candidates[i];// 注意可以重复读取当前的数,所以下一次start=ibacktarck(candidates,target,sum,i,track);// 递归回溯// 撤销选择sum-=candidates[i];track.remove(track.size() - 1);}}
}

分析

如果不需要记录选择的路径,只需返回结果总数sum+=candidates[i];  // 做选择
backtarck(candidates,target,sum,i,track);// 递归回溯
sum-=candidates[i];// 撤销选择或者将sum+candidates[i]直接写在函数里面
backtarck(candidates,target,sum+candidates[i],i,track);// 递归回溯
  • 剪枝
去掉if (sum > target) {return;}修改
for(int i=start;i<candidates.length && sum+candidates[i]<= target;i++)

40.组合总和 II ***

【参考:40. 组合总和 II - 力扣(LeetCode)】

使用used[]去重(具有普适性)

【参考:代码随想录# 40.组合总和II】

我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:

  • used[i - 1] == 1,说明同一树枝candidates[i - 1]使用过
  • used[i - 1] == 0,说明同一树层candidates[i - 1]使用过(前一个树枝使用过)

前提:为了将重复的数字都放到一起,要先进行排序

boolean[] used = new boolean[n];// 默认为false

class Solution {List<List<Integer>> res=new ArrayList<>();List<Integer> track=new ArrayList<>(); //走过的路径public List<List<Integer>> combinationSum2(int[] candidates, int target) {Arrays.sort(candidates);// 先排序//加标志数组,用来辅助判断同层节点是否已经遍历int[] used = new int[candidates.length];// 默认为0backtarck(candidates,target,0,0,used);return res;}public void backtarck(int[] candidates,int target,int sum,int start,int[] used){if (sum > target) {return;}if(sum==target){res.add(new ArrayList<>(track));return;}for(int i=start;i<candidates.length;i++){if (i > 0 && candidates[i] == candidates[i - 1] && used[i-1] == 0) {continue;}// 做选择track.add(candidates[i]);sum+=candidates[i];used[i]=1;// 递归回溯backtarck(candidates,target,sum,i+1,used);// 下一次start=i+1// 撤销选择used[i]=0;            sum-=candidates[i];track.remove(track.size() - 1);}}
}

直接使用start去重

【参考:回溯算法 + 剪枝(Java、Python) - 组合总和 II - 力扣(LeetCode)】

解释语句: if cur > begin and candidates[cur-1] == candidates[cur] 是如何避免重复的。
【参考:回溯算法 + 剪枝(Java、Python) - 组合总和 II 高赞评论 - 力扣(LeetCode)】

class Solution {List<List<Integer>> res=new ArrayList<>();public List<List<Integer>> combinationSum2(int[] candidates, int target) {List<Integer> track=new ArrayList<>(); //走过的路径Arrays.sort(candidates);backtarck(candidates,target,0,0,track);return res;}public void backtarck(int[] candidates,int target,int sum,int start,List<Integer> track){if (sum > target) {return;}if(sum==target){res.add(new ArrayList<>(track));return;}for(int i=start;i<candidates.length;i++){// 这里去重是关键if (i > start && candidates[i] == candidates[i - 1]) {continue;}// 做选择track.add(candidates[i]);sum+=candidates[i];// 递归回溯backtarck(candidates,target,sum,i+1,track);// 下一次start=i+1// 撤销选择sum-=candidates[i];track.remove(track.size() - 1);}}
}

17. 电话号码的字母组合

【参考:17. 电话号码的字母组合 - 力扣(LeetCode)】

class Solution {//初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""String letterMap[] = {" ",    //0"",     //1"abc",  //2"def",  //3"ghi",  //4"jkl",  //5"mno",  //6"pqrs", //7"tuv",  //8"wxyz"  //9};List<String> reslut=new ArrayList<>();StringBuilder sb=new StringBuilder();public List<String> letterCombinations(String digits) {if(digits.length()==0){return reslut;}backTracking(digits,0);return reslut;}// 下标index表示digits中第index+1个字符public void backTracking(String digits,int index){if(index==digits.length()){reslut.add(sb.toString());// 添加return;}int num=digits.charAt(index)- '0';// 比如 '2'-'0'= 2String letter=letterMap[num];// 数字对于的字符串for(char c: letter.toCharArray()){sb.append(c);backTracking(digits,index+1);// 递归sb.deleteCharAt(sb.length()-1);// 回溯}}
}

131. 分割回文串

【参考:131. 分割回文串 - 力扣(LeetCode)】

【参考:代码随想录# 131.分割回文串】

class Solution {List<List<String>> result = new ArrayList<>();List<String> path = new ArrayList<>(); public List<List<String>> partition(String s) {backtarck(s, 0);return result;}public void backtarck(String s, int start) {if (start > s.length()) {return;}if (start == s.length()) {result.add(new ArrayList<>(path));return;}for (int i = start; i < s.length(); i++) {// 不是回文则跳过if (!isPalindrome(s, start, i)) {continue;}// 做选择path.add(s.substring(start, i + 1));// +1 是因为substring函数是[)// 递归回溯backtarck(s, i + 1);// 撤销选择path.remove(path.size() - 1);}}public boolean isPalindrome(String s, int left, int right) {while (left < right) {if (s.charAt(left) != s.charAt(right)) {return false;}left++;right--;}return true;}
}

93. 复原 IP 地址(难)

【参考:93. 复原 IP 地址 - 力扣(LeetCode)】

【参考:代码随想录# 93.复原IP地址】

class Solution {List<String> result = new ArrayList<>();List<String> path = new ArrayList<>(); //走过的路径public List<String> restoreIpAddresses(String s) {// 如果长度不符合int len = s.length();if (len < 4 || len > 12) {return result;}backtarck(s, 0, 0);return result;}public void backtarck(String s, int start, int pointNum) {if (pointNum == 3) {// 逗点数量为3时,分隔结束// 判断第四段⼦字符串是否合法,如果合法就放进result中if (isValid(s, start, s.length() - 1)) {result.add(s);}return;}for (int i = start; i < s.length(); i++) {if (!isValid(s, start, i)) {break;} else {// substring:1.[beginIndex, endIndex) 2.beginIndex至末尾s = s.substring(0, i + 1) + "." + s.substring(i + 1); //在str的后⾯插⼊⼀个逗点pointNum++;// 点的数量加一backtarck(s, i + 2, pointNum);// 插⼊点之后下⼀个⼦串的起始位置为i+2pointNum--;// 回溯s = s.substring(0, i + 1) + s.substring(i + 2);// 回溯删掉点}}}// 判断字符串s在左闭⼜闭区间[start, end]所组成的数字是否合法private Boolean isValid(String s, int start, int end) {if (start > end) {return false;}if (s.charAt(start) == '0' && start != end) { // 0开头的数字不合法return false;}int num = 0;for (int i = start; i <= end; i++) {if (s.charAt(i) > '9' || s.charAt(i) < '0') { // 遇到⾮数字字符不合法return false;}num = num * 10 + (s.charAt(i) - '0');if (num > 255) { // 如果⼤于255了不合法return false;}}return true;}
}

491. 递增子序列(难)

【参考:491. 递增子序列 - 力扣(LeetCode)】

【参考:代码随想录# 491.递增子序列】

需要去重,但不能重新排序

set去重那块不太好理解,待回顾
【参考:代码随想录# 回溯算法去重问题的另一种写法】

class Solution {List<List<Integer>> result = new ArrayList<>();List<Integer> path = new ArrayList<>();public List<List<Integer>> findSubsequences(int[] nums) {backtrack(nums, 0);return result;}public void backtrack(int[] nums, int start) {if (path.size() > 1) {result.add(new ArrayList<>(path));// 这里不要return}Set<Integer> set = new HashSet<>();// 仅对本层元素进行去重for (int i = start; i < nums.length; i++) {if ((!path.isEmpty() && nums[i] < path.get(path.size() - 1))|| set.contains(nums[i])) {continue;}set.add(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了path.add(nums[i]);backtrack(nums, i + 1);path.remove(path.size() - 1);// set.remove(nums[i]);// 不需要该行,每次backtrack都会重置set}}
}

46. 全排列 ***

【参考:46. 全排列 - 力扣(LeetCode)】

【参考:回溯算法解题套路框架 :: labuladong的算法小抄】

// labuladong
List<List<Integer>> res = new LinkedList<>();/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {// 记录「路径」LinkedList<Integer> track = new LinkedList<>();backtrack(nums, track);return res;
}// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {// 触发结束条件if (track.size() == nums.length) {res.add(new LinkedList(track));return;}for (int i = 0; i < nums.length; i++) {// 排除不合法的选择if (track.contains(nums[i])) {continue;}// 做选择track.add(nums[i]);// 进入下一层决策树backtrack(nums, track);// 取消选择track.removeLast();}
}

【参考:代码随想录# 46.全排列】

class Solution {List<List<Integer>> result=new ArrayList<>();List<Integer> path=new ArrayList<>(); public List<List<Integer>> permute(int[] nums) {//加标志数组,用来辅助判断int[] used = new int[nums.length];// 默认为0backtarck(nums,used);return result;}public void backtarck(int[] nums,int[] used){if(path.size()==nums.length){result.add(new ArrayList<>(path));return;}for(int i=0;i<nums.length;i++){if (used[i] == 1) {// path中已经收录的元素,直接跳过continue;}// 做选择path.add(nums[i]);used[i]=1;backtarck(nums,used);// 递归回溯// 撤销选择used[i]=0;            path.remove(path.size() - 1);}}
}

47. 全排列 II

【参考:47. 全排列 II - 力扣(LeetCode)】

需要去重

class Solution {List<List<Integer>> result=new ArrayList<>();List<Integer> path=new ArrayList<>(); public List<List<Integer>> permuteUnique(int[] nums) {Arrays.sort(nums);// 先排序//加标志数组,用来辅助判断int[] used = new int[nums.length];// 默认为0backtarck(nums,used);return result;}public void backtarck(int[] nums,int[] used){if(path.size()==nums.length){result.add(new ArrayList<>(path));return;}for(int i=0;i<nums.length;i++){// 去重if (i > 0 && nums[i] == nums[i - 1] && used[i-1] == 0) {continue;}if (used[i] == 1) {// path中已经收录的元素,直接跳过continue;}// 做选择path.add(nums[i]);used[i]=1;backtarck(nums,used);// 递归回溯// 撤销选择used[i]=0;            path.remove(path.size() - 1);}}
}

困难

51. N 皇后 ***

【参考:51. N 皇后 - 力扣(LeetCode)】

【参考:回溯算法解题套路框架 :: labuladong的算法小抄】

这个问题本质上跟全排列问题差不多,决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行的任意一列放置一个皇后。

因为 C++ 代码对字符串的操作方便一些,所以这道题我用 C++ 来写解法,直接套用回溯算法框架:

vector<vector<string>> res;/* 输入棋盘边长 n,返回所有合法的放置 */
vector<vector<string>> solveNQueens(int n) {// '.' 表示空,'Q' 表示皇后,初始化空棋盘。vector<string> board(n, string(n, '.'));backtrack(board, 0);return res;
}// 路径:board 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件:row 超过 board 的最后一行
void backtrack(vector<string>& board, int row) {// 触发结束条件if (row == board.size()) {res.push_back(board);return;}int n = board[row].size();for (int col = 0; col < n; col++) {// 排除不合法选择if (!isValid(board, row, col)) {continue;}// 做选择board[row][col] = 'Q';// 进入下一行决策backtrack(board, row + 1);// 撤销选择board[row][col] = '.';}
}/* 是否可以在 board[row][col] 放置皇后? */
bool isValid(vector<string>& board, int row, int col) {int n = board.size();// 检查列是否有皇后互相冲突for (int i = 0; i < n; i++) {if (board[i][col] == 'Q')return false;}// 检查右上方是否有皇后互相冲突for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (board[i][j] == 'Q')return false;}// 检查左上方是否有皇后互相冲突for (int i = row - 1, j = col - 1;i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 'Q')return false;}return true;
}

只想要一个答案,怎么办呢?
只要找到一个答案,for 循环的后续递归穷举都会被阻断。

// 函数找到一个答案后就返回 true
bool backtrack(vector<string>& board, int row) {// 触发结束条件if (row == board.size()) {res.push_back(board);return true;}...for (int col = 0; col < n; col++) {...board[row][col] = 'Q';if (backtrack(board, row + 1))return true;board[row][col] = '.';}return false;
}

【参考:代码随想录# 第51题. N皇后】

class Solution {List<List<String>> result = new ArrayList<>();List<String> chessboard = new ArrayList<>();public List<List<String>> solveNQueens(int n) {// "." 表示空,"Q"表示皇后,初始化棋盘char[][] board = new char[n][n];for (char[] c : board) {Arrays.fill(c, '.');}backtrack(n, 0, board);return result;}// row 记录当前遍历到棋盘的第row+1层public void backtrack(int n, int row, char[][] board) {// 准备开始遍历到n+1行时,表明此时棋盘已铺满(row从0开始)if (row == n) {result.add(charToList(board));}// 按行遍历for (int col = 0; col < n; col++) {if (isVaild(row, col, n, board)) {board[row][col] = 'Q'; // 放置皇后backtrack(n, row + 1, board);// 递归下一行board[row][col] = '.'; // 回溯 撤销皇后}}}public boolean isVaild(int row, int col, int n, char[][] board) {// 检查列(正上方)for (int i = 0; i < row; i++) {if (board[i][col] == 'Q')return false;}// 检查左上方for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {if (board[i][j] == 'Q')return false;}// 检查右上方for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {if (board[i][j] == 'Q')return false;}return true;}public List<String> charToList(char[][] board) {List<String> list = new ArrayList<>();for (char[] c : board) {// System.out.println(String.valueOf(c));list.add(String.valueOf(c));}// System.out.println();return list;}
}
.Q..
...Q
Q...
..Q...Q.
Q...
...Q
.Q..

37. 解数独

【参考:37. 解数独 - 力扣(LeetCode)】

【参考:回溯算法最佳实践:解数独 :: labuladong的算法小抄】

【参考:代码随想录# 37. 解数独】

有点没看懂 待回顾

class Solution {public void solveSudoku(char[][] board) {solveSudokuHelper(board);}private boolean solveSudokuHelper(char[][] board){//「一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,// 一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!」for (int i = 0; i < 9; i++){ // 遍历行for (int j = 0; j < 9; j++){ // 遍历列if (board[i][j] != '.'){ // 跳过原始数字continue;}for (char k = '1'; k <= '9'; k++){ // (i, j) 这个位置放k是否合适if (isValidSudoku(i, j, k, board)){board[i][j] = k;if (solveSudokuHelper(board)){ // 如果找到合适一组立刻返回return true;}board[i][j] = '.';}}// 9个数都试完了,都不行,那么就返回falsereturn false;// 因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!// 那么会直接返回, 「这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!」}}// 遍历完没有返回false,说明找到了合适棋盘位置了return true;}/*** 判断棋盘是否合法有如下三个维度:*     同行是否重复*     同列是否重复*     9宫格里是否重复*/private boolean isValidSudoku(int row, int col, char val, char[][] board){// 同行是否重复for (int i = 0; i < 9; i++){if (board[row][i] == val){return false;}}// 同列是否重复for (int j = 0; j < 9; j++){if (board[j][col] == val){return false;}}// 9宫格里是否重复int startRow = (row / 3) * 3;int startCol = (col / 3) * 3;for (int i = startRow; i < startRow + 3; i++){for (int j = startCol; j < startCol + 3; j++){if (board[i][j] == val){return false;}}}return true;}
}

【LeetCode】回溯 N皇后(DFS、子集、组合问题)相关推荐

  1. 【LeetCode】【HOT】39. 组合总和(回溯)

    [LeetCode][HOT]39. 组合总和 文章目录 [LeetCode][HOT]39. 组合总和 package hot;import java.util.ArrayList; import ...

  2. LeetCode 例题精讲 | 08 排列组合问题:回溯法的候选集合

    点击关注上方"五分钟学算法", 设为"置顶或星标",第一时间送达干货. 转自面向大象编程 本期例题:LeetCode 46 - Permutations[1]( ...

  3. 回溯算法团灭子集、排列、组合问题

    回溯算法团灭子集.排列.组合问题 一.子集 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = [1,2,3] ...

  4. Leetcode回溯算法经典题目总结

    回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 "回溯" 返回,尝试别的路径.回溯法是一种选优搜索法,按选优条件向前搜索 ...

  5. 算法训练Day24 | 回溯算法理论基础;LeetCode77.组合(经典的回溯问题)

    目录 回溯算法理论基础 1. 什么是回溯法 2. 回溯法的效率 3. 回溯法解决的问题 4. 如何理解回溯法 5. 回溯法模板-- 回溯三部曲 6. 总结 LeetCode77.组合 1. 思路 2. ...

  6. 【子集/组合/全排列】C语言框架

    子集,组合,全排列三类问题非常相似.题目涉及到数组中的数字是否有重复.如果有重复时,需要额外判断,例如子集II问题中,先对数组排序,回溯过程中判断相邻两个数字是否相同:全排列问题中需要判断cur位置是 ...

  7. 数字拆分问题算法回溯_回溯算法:求子集问题!

    给「代码随想录」一个星标吧! ❝ 认识本质之后,这就是一道模板题 通知:我将公众号文章和学习相关的资料整理到了Github :https://github.com/youngyangyang04/le ...

  8. 八十四、Python | Leetcode回溯算法系列

    @Author:Runsen @Date:2020/7/7 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  9. 日拱一卒——LeetCode 51.N皇后

    ​ 大家好呀,今天是今天为大家带来的LeetCode的题目是LeetCode 51.N皇后问题,算是一道很经典的题目,也是一道难度不低的题目,但是只要我们掌握并理解了判断的逻辑,并且在代码编写上进行注 ...

最新文章

  1. 数字图像处理1:基本概念
  2. 3.推荐系统(矩阵分解)
  3. mysql 优化器代码_Mysql查询优化器
  4. 多重签名(Multisig)
  5. linux grep -v反向搜索:不显示目标字符串
  6. 回溯法(其实是递归)
  7. wxWidgets:wxObjectDataPtr< T >类模板用法
  8. js 单页面ajax缓存策略,浅谈ajax的缓存机制---IE浏览器方面
  9. path manipulation怎么解决_干货!终于!解决macOS下pyenv安装python3.8.2缺少tkinter模块的问题!...
  10. ps -ef和ps aux
  11. C# list导出Excel(二)
  12. Javascript:json删除键为指定数据的值
  13. 阿里云祝顺民:未来的网络是云网一体,应用感知不到网络的存在
  14. 12306 脱库疑云:410 万用户数据仅售 20 美元!
  15. 立体几何——球缺问题
  16. 理解的三极管输出特性曲线图的放大区和饱和区,利用∆Ic小于∆βIb差值比较曲线左移右移关系
  17. php doctrine,Doctrine学习笔记 —— 1
  18. 将视频文件旋转90°的方法
  19. android电子书大全 下载
  20. 【安全资讯】全球上市公司财报中“网络安全”的提及次数出现显著增长

热门文章

  1. HDFS名字空间(NameSpace)
  2. 坚果的2022年终总结
  3. Java练习题十四期:不要二
  4. pandas操作excel,matplotlib.pyplot画图插入到excel,处理复杂excel简单练习
  5. linux内核usleep,Linux下的usleep函数
  6. 通用搜索引擎的垂直化倾向
  7. 第十一讲 项目3 买五赠一 买二十赠五 优惠计算
  8. Spark教程——(10)Spark SQL读取Phoenix数据本地执行计算
  9. 关注ERP项目中的隐含成本
  10. 计算机毕业设计_基于SSM的医院预约挂号系统设计与实现