【LeetCode】14.回溯算法系列

总目录:

LeetCode系列导航目录

 

0.理论基础

0.1.要点

(0)如果解决一个问题有多个步骤,每一个步骤有多种方法,题目又要我们找出所有的方法,那肯定要穷举,可以使用回溯算法。

(1)回溯是递归的副产品,只要有回溯就会有递归。

(2)回溯法也可以叫做回溯搜索法,它是一种穷举搜索的方式,效率慢。一些问题只能通过穷举解决,可以通过适当的剪枝来加快效率。

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

0.2.问题类型

常见问题类型:

(1)组合问题:N个数里面按一定规则找出k个数的集合

(2)切割问题:一个字符串按一定规则有几种切割方式

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

(4)排列问题:N个数按一定规则全排列,有几种排列方式

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

0.3.方法模板

for循环横向遍历,递归纵向遍历,回溯不断调整结果集

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

 

1.组合

1.1.问题描述

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按任何顺序返回答案。

链接:https://leetcode.cn/problems/combinations/

1.2.要点

树形结构,深度搜索

剪枝:数量超过k的跳过

1.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<int>> result;
 4     vector<int> path;
 5     void backtracking(int n, int k, int startIndex) {
 6         if (path.size() == k) {
 7             result.push_back(path);
 8             return;
 9         }
10         for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方
11             path.push_back(i); // 处理节点
12             backtracking(n, k, i + 1);
13             path.pop_back(); // 回溯,撤销处理的节点
14         }
15     }
16 public:
17 
18     vector<vector<int>> combine(int n, int k) {
19         backtracking(n, k, 1);
20         return result;
21     }
22 };
View Code

 

2.组合总和3

2.1.问题描述

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
(1)只使用数字1到9;
(2)每个数字最多使用一次 ;
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
链接:https://leetcode.cn/problems/combination-sum-iii

2.2.要点

在普通组合问题上添加一个总和匹配的目标

剪枝:总和超过n、数量超过k的跳过

2.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<int>> ret;
 4     vector<int> temp;
 5     int curSum=0,targetSum=0,targetCnt=0;
 6     void backTracking(int start){        
 7         if(curSum>targetSum){
 8             return;
 9         }
10         if(temp.size()==targetCnt){
11             if(curSum==targetSum){
12                 ret.push_back(temp);                
13             }
14             return;
15         }
16 
17         for(int i=start;i<=(9-(targetCnt-temp.size())+1);i++){
18             temp.push_back(i);
19             curSum+=i;
20             backTracking(i+1);
21             temp.pop_back();
22             curSum-=i;
23         }
24     }
25 
26 public:
27     vector<vector<int>> combinationSum3(int k, int n) {        
28         targetSum=n;
29         targetCnt=k;        
30         backTracking(1);
31         return ret;
32     }
33 };
View Code

 

3.电话号码的字母组合

3.1.问题描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

链接:https://leetcode.cn/problems/letter-combinations-of-a-phone-number

3.2.要点

全排列组合

3.3.代码实例

 1 class Solution {
 2 private:
 3     vector<string> code{"","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
 4     vector<string> ret;
 5     string srcStr;    
 6     int tgtLen=0;
 7     string temp;
 8     void backTracking(int start){
 9         if(temp.length()==tgtLen){
10             ret.emplace_back(temp);
11             return;
12         }
13 
14         //防止code访问越界
15         if(start>=tgtLen){
16             return;
17         }
18         int codeId=srcStr[start]-'1';
19         if(codeId<0||codeId>9){
20             return;
21         }
22         string curCode=code[codeId];
23         for(auto& c:curCode){
24             temp+=c;
25             backTracking(start+1);
26             temp=temp.substr(0,temp.length()-1);
27         }
28     }
29 public:
30     vector<string> letterCombinations(string digits) {
31         srcStr=digits;
32         tgtLen=digits.length();
33         if(tgtLen<=0){
34             return ret;
35         }
36 
37         backTracking(0);
38         return ret;
39     }
40 };
View Code

 

4.组合总和1

4.1.问题描述

给你一个 无重复元素 的正整数数组 candidates 和一个目标正整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
链接:https://leetcode.cn/problems/combination-sum

4.2.要点

特点是允许原地踏步、不允许后退

4.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<int>> ret;
 4     vector<int> temp;
 5     int curSum=0;
 6     int tgtSum=0;
 7     int dataLen=0;
 8     int stage=0;
 9     void backTracking(vector<int>& candidates,int start){
10         if(curSum>tgtSum){
11             return;
12         }
13         if(curSum==tgtSum){
14             ret.push_back(temp);
15             return;
16         }
17         for(int i=start;i<dataLen;i++){
18             curSum+=candidates[i];
19             temp.push_back(candidates[i]);
20             backTracking(candidates,i);
21             curSum-=candidates[i];
22             temp.pop_back();
23         }
24     }
25 
26 public:
27     vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
28         tgtSum=target;
29         dataLen=candidates.size();
30         backTracking(candidates,0);
31         
32         return ret;
33     }
34 };
View Code

 

5.组合总和2

5.1.问题描述

给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
链接:https://leetcode.cn/problems/combination-sum-ii

5.2.要点

重点要求:集合(数组candidates)有重复元素,但还不能有重复的组合.

为了跳过重复元素造成的重复组合,有固定的范式:

(1)排序;

(2)在循环中使用以下代码,也就是不允许在广度上重复、允许在深度上重复:

if(i>start&&candidates[i]==candidates[i-1]){
     continue;
}

5.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<int>> ret;
 4     vector<int> temp;
 5     int curSum=0;
 6     int tgtSum=0;
 7     int dataLen=0;
 8     void backTracking(vector<int>& candidates,int start){
 9         if(curSum>tgtSum){
10             return;
11         }
12         if(curSum==tgtSum){
13             ret.push_back(temp);
14             return;
15         }
16         for(int i=start;i<dataLen;i++){     
17             if(i>start&&candidates[i]==candidates[i-1]){
18                 continue;
19             }
20 
21             curSum+=candidates[i];
22             temp.push_back(candidates[i]);
23             backTracking(candidates,i+1);
24             curSum-=candidates[i];
25             temp.pop_back();            
26         }
27     }
28 
29 public:
30     vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
31         tgtSum=target;
32         dataLen=candidates.size();
33         sort(candidates.begin(),candidates.end());
34         backTracking(candidates,0);
35         
36         return ret;
37     }
38 };
View Code

 

6.分割回文串

6.1.问题描述

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

回文串 是正着读和反着读都一样的字符串。

链接:https://leetcode.cn/problems/palindrome-partitioning/

6.2.要点

先画树状图,再依次解决以下几个问题:

(1)切割问题可以抽象为组合问题

(2)如何模拟那些切割线

(3)切割问题中递归如何终止

(4)在递归循环中如何截取子串

(5)如何判断回文

解析:https://github.com/hitwzy/leetcode-master/blob/master/problems/0131.%E5%88%86%E5%89%B2%E5%9B%9E%E6%96%87%E4%B8%B2.md

6.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<string>> result;
 4     vector<string> path; // 放已经回文的子串
 5     void backtracking (const string& s, int startIndex) {
 6         // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
 7         if (startIndex >= s.size()) {
 8             result.push_back(path);
 9             return;
10         }
11         for (int i = startIndex; i < s.size(); i++) {
12             //指定段不是回文
13             if (!isPalindrome(s, startIndex, i)){
14                 continue;
15             }
16 
17             // 获取[startIndex,i]在s中的子串,回文串
18             string str = s.substr(startIndex, i - startIndex + 1);
19             path.push_back(str);
20             
21             backtracking(s, i + 1); // 寻找i+1为起始位置的子串
22             path.pop_back(); // 回溯过程,弹出本次已经填在的子串
23         }
24     }
25     bool isPalindrome(const string& s, int start, int end) {
26         for (int i = start, j = end; i < j; i++, j--) {
27             if (s[i] != s[j]) {
28                 return false;
29             }
30         }
31         return true;
32     }
33 public:
34     vector<vector<string>> partition(string s) {
35         result.clear();
36         path.clear();
37         backtracking(s, 0);
38         return result;
39     }
40 };
View Code

 

7.复原IP地址

7.1.问题描述

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。
例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
链接:https://leetcode.cn/problems/restore-ip-addresses

7.2.要点

切割问题,切一段判断一段

本问题主要是判断的规则较细

7.3.代码实例

 1 #include <string>
 2 using namespace std;
 3 class Solution {
 4 private:
 5     vector<string> ret;
 6     string temp;
 7     int pointCnt=0;
 8     void backTracking(string& s,int startIndex){
 9         if(startIndex>=s.length()){
10             if(pointCnt<4){
11                 return;
12             }
13             temp.pop_back();//去掉最后一个句号
14             ret.push_back(temp);
15             temp.push_back('.');
16             return;
17         }
18 
19         for(int i=startIndex;i<s.length();i++){
20             if(pointCnt==3){
21                 i=s.length()-1;
22             }
23             if(!isPartOfIp(s,startIndex,i)){
24                 continue;
25             }
26 
27             temp+=s.substr(startIndex,(i-startIndex+1));
28             temp+='.';
29             pointCnt++;
30 
31             backTracking(s,i+1);
32 
33             temp.pop_back();
34             pointCnt--;
35             while(temp.length()>0&&temp[temp.length()-1]!='.'){
36                 temp.pop_back();
37             }
38         }
39 
40     }
41     bool isPartOfIp(string s,int start,int end){        
42         //检查数据长度在1~3之间
43         if((end-start+1)<=0||(end-start+1)>3){
44             return false;
45         }
46         //检查非法字符
47         for(int i=start;i<=end;i++){
48             if(s[i]<'0'||s[i]>'9'){
49                 return false;
50             }
51         }
52         //是否超限
53         if(end>start&&s[start]=='0'){
54             return false;
55         }
56         int num=atoi(s.substr(start,(end-start+1)).c_str());
57         if(num>255){
58             return false;
59         }
60 
61         return true;
62     }
63 public:
64     vector<string> restoreIpAddresses(string s) {
65         if (s.size() < 4 || s.size() > 12) return ret; // 算是剪枝了
66         backTracking(s,0);
67         return ret;
68     }
69 };
View Code

 

8.子集1

8.1.问题描述

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

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

链接:https://leetcode.cn/problems/subsets/

8.2.要点

1掩码

通过bit位来表示某数字是否出现在集合中,遍历全排列组合的空间

2回溯法

观察树结构,注意“取节点而不是取叶子”这个概念

 

 

8.3.代码实例

掩码

 1 class Solution {
 2 public:
 3     vector<int> t;
 4     vector<vector<int>> ans;
 5 
 6     vector<vector<int>> subsets(vector<int>& nums) {
 7         int n = nums.size();
 8         for (int mask = 0; mask < (1 << n); ++mask) {
 9             t.clear();
10             for (int i = 0; i < n; ++i) {
11                 if (mask & (1 << i)) {
12                     t.push_back(nums[i]);
13                 }
14             }
15             ans.push_back(t);
16         }
17         return ans;
18     }
19 };
View Code

回溯

 1 class Solution {
 2 private:
 3     vector<vector<int>> result;
 4     vector<int> path;
 5     void backtracking(vector<int>& nums, int startIndex) {
 6         result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
 7         if (startIndex >= nums.size()) { // 终止条件可以不加
 8             return;
 9         }
10         for (int i = startIndex; i < nums.size(); i++) {
11             path.push_back(nums[i]);
12             backtracking(nums, i + 1);
13             path.pop_back();
14         }
15     }
16 public:
17     vector<vector<int>> subsets(vector<int>& nums) {
18         result.clear();
19         path.clear();
20         backtracking(nums, 0);
21         return result;
22     }
23 };
View Code

 

9.子集2

9.1.问题描述

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
链接:https://leetcode.cn/problems/subsets-ii

9.2.要点

回溯,仅添加一个横向搜索时的去重操作

9.3.代码实例

 1 class Solution {
 2 private:
 3     vector<vector<int>> result;
 4     vector<int> path;
 5     void backtracking(vector<int>& nums, int startIndex) {
 6         result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
 7         if (startIndex >= nums.size()) { // 终止条件可以不加
 8             return;
 9         }
10         for (int i = startIndex; i < nums.size(); i++) {
11             if(i>startIndex&&nums[i]==nums[i-1]){
12                 continue;
13             }
14             path.push_back(nums[i]);
15             backtracking(nums, i + 1);
16             path.pop_back();
17         }
18     }
19 public:
20     vector<vector<int>> subsetsWithDup(vector<int>& nums) {
21         result.clear();
22         path.clear();
23         sort(nums.begin(),nums.end());
24         backtracking(nums, 0);
25         return result;
26     }
27 };
View Code

 

10.全排列

10.1.问题描述

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

链接:https://leetcode.cn/problems/permutations/

10.2.要点

出现过的元素不能再被遍历到,标志位来记录使用情况。

10.3.代码实例

基于标志位的

 1 class Solution {
 2 public:
 3     vector<vector<int>> result;
 4     vector<int> path;
 5     void backtracking (vector<int>& nums, vector<bool>& used) {
 6         // 此时说明找到了一组
 7         if (path.size() == nums.size()) {
 8             result.push_back(path);
 9             return;
10         }
11         for (int i = 0; i < nums.size(); i++) {
12             if (used[i] == true) continue; // path里已经收录的元素,直接跳过
13             used[i] = true;
14             path.push_back(nums[i]);
15             backtracking(nums, used);
16             path.pop_back();
17             used[i] = false;
18         }
19     }
20     vector<vector<int>> permute(vector<int>& nums) {
21         result.clear();
22         path.clear();
23         vector<bool> used(nums.size(), false);
24         backtracking(nums, used);
25         return result;
26     }
27 };
View Code

基于交换的

 1 class Solution {
 2 public:
 3     vector<vector<int>> result;
 4     //vector<int> path;
 5     void backtracking (vector<int>& nums, int start) {
 6         // 此时说明找到了一组
 7         if (start >= nums.size()) {
 8             result.push_back(nums);
 9             return;
10         }
11         for (int i = start; i < nums.size(); ++i) {
12             swap(nums[start],nums[i]);
13             backtracking(nums, start+1);
14             swap(nums[start],nums[i]);
15         }
16     }
17     vector<vector<int>> permute(vector<int>& nums) {
18         result.clear();
19         backtracking(nums,0);
20         return result;
21     }
22 };
View Code

 

11.带重复的全排列

11.1.问题描述

给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
 [1,2,1],
 [2,1,1]]
链接:https://leetcode.cn/problems/permutations-ii

11.2.要点

要注意是不允许在树枝上重复还是不允许在树层间重复

11.3.代码实例

 1 class Solution {
 2 public:
 3     vector<vector<int>> result;
 4     vector<int> path;
 5     void backtracking (vector<int>& nums, vector<bool>& used) {
 6         // 此时说明找到了一组
 7         if (path.size() == nums.size()) {
 8             result.push_back(path);
 9             return;
10         }
11         for (int i = 0; i < nums.size(); i++) {
12             // used[i - 1] == true,说明同一树枝nums[i - 1]使用过
13             // used[i - 1] == false,说明同一树层nums[i - 1]使用过 
14             // 如果同一树层nums[i - 1]使用过则直接跳过
15             if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {
16                 continue;
17             }
18             if (used[i] == true) continue; // path里已经收录的元素,直接跳过
19             used[i] = true;
20             path.push_back(nums[i]);
21             backtracking(nums, used);
22             path.pop_back();
23             used[i] = false;
24         }
25     }
26     vector<vector<int>> permuteUnique(vector<int>& nums) {
27         result.clear();
28         path.clear();
29         vector<bool> used(nums.size(), false);
30         
31         sort(nums.begin(),nums.end());
32         backtracking(nums, used);
33         return result;
34     }
35 };
View Code

 

12.递增子序列

12.1.问题描述

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
链接:https://leetcode.cn/problems/non-decreasing-subsequences

12.2.要点

注意,这里不能排序,前面已经用过的去重方法不再适用。

因此要想在层中去重得用特殊一点的方法,如哈希查询是否某元素已使用过

12.3.代码实例

 1 // 版本一
 2 class Solution {
 3 private:
 4     vector<vector<int>> result;
 5     vector<int> path;
 6     void backtracking(vector<int>& nums, int startIndex) {
 7         if (path.size() > 1) {
 8             result.push_back(path);
 9             // 注意这里不要加return,要取树上的节点
10         }
11         unordered_set<int> uset; // 使用set对本层元素进行去重
12         for (int i = startIndex; i < nums.size(); i++) {
13             if ((!path.empty() && nums[i] < path.back())
14                     || uset.find(nums[i]) != uset.end()) {
15                     continue;
16             }
17             uset.insert(nums[i]); // 记录这个元素在本层用过了,本层后面不能再用了
18             path.push_back(nums[i]);
19             backtracking(nums, i + 1);
20             path.pop_back();
21         }
22     }
23 public:
24     vector<vector<int>> findSubsequences(vector<int>& nums) {
25         result.clear();
26         path.clear();
27         backtracking(nums, 0);
28         return result;
29     }
30 };
View Code

 

13.重新安排行程问题——暂略

13.1.问题描述

111

13.2.要点

222

13.3.代码实例

333

 

14.N皇后问题

14.1.问题描述

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
链接:https://leetcode.cn/problems/n-queens

14.2.要点

回溯尝试问题,判断是否冲突即可

14.3.代码实例

 1 class Solution {
 2 private:
 3     int dim=0;
 4     vector<vector<string>> ret;
 5     vector<vector<bool>> flags;
 6     void backTracking(int row){
 7         //到达一组的边界
 8         if(row>=dim){            
 9             save();
10             return;
11         }
12 
13         for(int i=0;i<dim;i++){
14             if(hasConflict(row,i)){
15                 continue;
16             }
17             flags[row][i]=true;
18             backTracking(row+1);
19             flags[row][i]=false;
20         }       
21     }
22     void save(){
23         vector<string> sln;
24         string temp;
25         for(int i=0;i<dim;i++){
26             for(int j=0;j<dim;j++){
27                 temp+=flags[i][j]?'Q':'.';
28             }
29             sln.push_back(temp);
30             temp.clear();
31         }
32         ret.push_back(sln);
33     }
34     bool hasConflict(const int row,const int col){
35         //检查行,没必要
36         
37         //检查列
38         for(int i=0;i<dim;i++){
39             if(i!=row && flags[i][col]){
40                 return true;
41             }
42         }
43 
44         int tgtRow=row,tgtCol=col;
45         //检查1条斜线
46         while(tgtRow>=0 && tgtCol>=0){         
47             tgtRow--;
48             tgtCol--;
49         }
50         tgtRow++;tgtCol++;
51         while(tgtRow<dim && tgtCol<dim){
52             if(tgtRow!=row && tgtCol!=col){
53                 if(flags[tgtRow][tgtCol]){
54                     return true;
55                 }
56             }            
57             tgtRow++;
58             tgtCol++;
59         }
60 
61         //检查另一条斜线
62         tgtRow=row,tgtCol=col;
63         while(tgtRow<dim && tgtCol>=0){         
64             tgtRow++;
65             tgtCol--;
66         }
67         tgtRow--;tgtCol++;
68         while(tgtRow>=0 && tgtCol<dim){
69             if(tgtRow!=row && tgtCol!=col){
70                 if(flags[tgtRow][tgtCol]){
71                     return true;
72                 }
73             }            
74             tgtRow--;
75             tgtCol++;
76         }
77 
78         return false;
79     }
80 public:
81     vector<vector<string>> solveNQueens(int n) {
82         dim=n;
83         flags.resize(n,vector<bool>(n,false));
84         backTracking(0);
85         return ret;
86     }
87 };
View Code

 

15.解数独问题

15.1.问题描述

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

(1)数字 1-9 在每一行只能出现一次。

(2)数字 1-9 在每一列只能出现一次。

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

数独部分空格内已填入了数字,空白格用 '.' 表示。

 

 链接:https://leetcode.cn/problems/sudoku-solver/

15.2.要点

一般的回溯问题是一个二维树状搜索空间,数独问题是一个树状的三维搜索空间。在m行n列上取x。横向遍历时需要2个for循环的嵌套

15.3.代码实例

 1 class Solution {
 2 private:
 3 bool backtracking(vector<vector<char>>& board) {
 4     for (int i = 0; i < board.size(); i++) {        // 遍历行
 5         for (int j = 0; j < board[0].size(); j++) { // 遍历列
 6             if (board[i][j] == '.') {
 7                 for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
 8                     if (isValid(i, j, k, board)) {
 9                         board[i][j] = k;                // 放置k
10                         if (backtracking(board)) return true; // 如果找到合适一组立刻返回
11                         board[i][j] = '.';              // 回溯,撤销k
12                     }
13                 }
14                 return false;  // 9个数都试完了,都不行,那么就返回false 
15             }                
16         }
17     }
18     return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
19 }
20 bool isValid(int row, int col, char val, vector<vector<char>>& board) {
21     for (int i = 0; i < 9; i++) { // 判断行里是否重复
22         if (board[row][i] == val) {
23             return false;
24         }
25     }
26     for (int j = 0; j < 9; j++) { // 判断列里是否重复
27         if (board[j][col] == val) {
28             return false;
29         }
30     }
31     int startRow = (row / 3) * 3;
32     int startCol = (col / 3) * 3;
33     for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
34         for (int j = startCol; j < startCol + 3; j++) {
35             if (board[i][j] == val ) {
36                 return false;
37             }
38         }
39     }
40     return true;
41 }
42 public:
43     void solveSudoku(vector<vector<char>>& board) {
44         backtracking(board);
45     }
46 };
View Code

 

16.总结

 

 

 

 

 

xxx.问题

xxx.1.问题描述

111

xxx.2.要点

222

xxx.3.代码实例

333

posted @ 2022-12-22 16:18  啊原来是这样呀  阅读(22)  评论(0编辑  收藏  举报