0%

LeetCode-Day13

37. 解数独

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

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

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。

img

img

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 ;
}
// board[i][j]非空,则考虑下一个位置
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++) {
// 判断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:

1
2
输入: [1,2,0]
输出: 3

示例 2:

1
2
输入: [3,4,-1,1]
输出: 2

示例 3:

1
2
输入: [7,8,9,11,12]
输出: 1

说明:

你的算法的时间复杂度应为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