37. 解数独
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| class Solution { public: bool solved = false; bool row[10][10], col[10][10], box[10][10]; void solveSudoku(vector<vector<char> >&board) { memset(row, false, sizeof(row)); memset(col, false, sizeof(col)); memset(box, false, sizeof(box));
for(int i = 0;i < 9;i++) { for(int j = 0;j < 9;j++) { if(board[i][j] == '.') { continue; } int index = 3 * (i / 3) + j / 3; int num = board[i][j] - '0'; row[i][num] = col[j][num] = box[index][num] = true; } } DFS(0, 0, board); }
void DFS(int i, int j, vector<vector<char> >& board) { if(solved) { return ; } if(i >= 9) { solved = true; return ; } if(board[i][j] != '.') { if(j < 8) { DFS(i, j + 1, board); } else if(j == 8) { DFS(i + 1, 0, board); } if(solved) { return ; } } else { int index = 3 * (i / 3) + j / 3; for(int num = 1;num <= 9;num++) { if(!row[i][num] && !col[j][num] && !box[index][num]) { board[i][j] = '0' + num; row[i][num] = col[j][num] = box[index][num] = true; if(j < 8) { DFS(i, j + 1, board); } else if(j == 8) { DFS(i + 1, 0, board); } if(!solved) { row[i][num] = col[j][num] = box[index][num] = false; board[i][j] = '.'; } } } } } };
|
解释:
二维数组row, col, box分别是用来记录每一行,每一列,每一宫出现过的数字
采用回溯的方法。
这里解释一下index = 3 * (i / 3) + j / 3
,这是用来解决,当前的(i, j)
是属于哪一宫的,
36_boxes_2.png](https://pic.leetcode-cn.com/5a7856c3c2a2185600b7cb5cd3fd50101281af7391a70a63293d82d62873aadd-36_boxes_2.png)
39. 组合总和
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
1 2 3 4 5 6
| 输入: candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]
|
示例 2:
1 2 3 4 5 6 7
| 输入: candidates = [2,3,5], target = 8, 所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: void backtrace(vector<int>& candidates, int target, vector<vector<int> >& ans, int weight, vector<int>& bag, int index) { if(weight == target) { vector<int> temp = bag; ans.push_back(temp); return ; } for(int i = index;i < candidates.size();i++) { if(weight + candidates[i] <= target) { weight += candidates[i]; bag.push_back(candidates[i]); backtrace(candidates, target, ans, weight, bag, i); weight -= candidates[i]; bag.pop_back(); } } }
vector<vector<int> > combinationSum(vector<int>& candidates, int target) { vector<vector<int> > ans; vector<int> bag; backtrace(candidates, target, ans, 0, bag, 0); return ans; } };
|
解释:这里的关键点就是数组是无重复的, 并且candidates
中的数字可以无限制重复被选取。
所以,在递归的时候,index
还是i
,并没有变成i+1
40. 组合总和II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
1 2 3 4 5 6 7 8
| 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
|
示例 2:
1 2 3 4 5 6
| 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution { public: void backtrace(vector<int>& candidates, int target, vector<vector<int> >& ans, int weight, vector<int>& bag, int index) { if(weight == target) { vector<int> temp = bag; ans.push_back(temp); return ; } for(int i = index;i < candidates.size();i++) { if(weight + candidates[i] <= target) { weight += candidates[i]; bag.push_back(candidates[i]); backtrace(candidates, target, ans, weight, bag, i + 1); weight -= candidates[i]; while(i + 1 < candidates.size() && candidates[i + 1] == candidates[i]) { i++; } bag.pop_back(); } } }
vector<vector<int> > combinationSum2(vector<int>& candidates, int target) { vector<vector<int> > ans; vector<int> bag; sort(candidates.begin(), candidates.end()); backtrace(candidates, target, ans, 0, bag, 0); return ans; } };
|
解释:
这个跟上一题的区别在于这里的candidate
数组中的数字是可以重复的,并且每个数字在每个组合中只能使用一次。在这里使用的trick就是先对candidate
数组进行排序。在回溯的时候,如果下一个要选取的数字和当前要回溯的数字是一样的话,就不选取,因为如果下一个要选取的数字和当前正要退回的数字是一样的话,会造成重复。
41. 缺失的第一个正数
给定一个未排序的整数数组,找出其中没有出现的最小的正整数。
示例 1:
示例 2:
示例 3:
说明:
你的算法的时间复杂度应为O(n),并且只能使用常数级别的空间。
方法一:先排序,后遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class Solution { public: int firstMissingPositive(vector<int>& nums) { if(nums.size() == 0) { return 1; } sort(nums.begin(), nums.end()); int index = 0; while(index < nums.size() && nums[index] <= 0) { index++; } if(index == nums.size() || nums[index] != 1) { return 1; } int min = 1; while(index < nums.size()) { if(nums[index] == min) { min++; } else if(nums[index] > min) { return min; } index++; } return min; } };
|
方法二:hash表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution { public: int firstMissingPositive(vector<int>& nums) { if(nums.size() == 0) { return 1; } int hash[nums.size() + 1] = {0}; for(int i = 0;i < nums.size();i++) { if(nums[i] <= 0 || nums[i] > nums.size()) { continue; } hash[nums[i]]++; } int i = 1; while(i <= nums.size() && hash[i] != 0) { i++; } if(i == nums.size() + 1) { return nums.size() + 1; } else { return i; } } };
|
解释:先遍历一遍数组,然后将小于1和大于nums.size()
的数字都过滤掉,然后统计nums[i]
出现的次数记录在hash表中。
遍历hash
,如果在中间遇到了hash
值为0的说明,这个就是要找的最小的缺失的整数,如果在最后才推出循环,说明,最小的整数应该不在hash
表中,而是nums
数组中最大的元素+1