目录
36. 有效的数独
37. 解数独
38. 外观数列
39. 组合总和
40. 组合总和 II
36. 有效的数独
题目描述
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例 1:
输入:
[ [“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”] ]
输出: true
示例 2:
输入:
[ [“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”] ]
输出: false
解释: 除了第一行的第一个数字从
5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
一个有效的数独(部分已被填充)不一定是可解的。只需要根据以上规则,验证已经填入的数字是否有效即可。给定数独序列只包含数字 1-9和字符 ‘.’ 。给定数独永远是 9x9 形式的。
思路:
遍历数独。
检查看到每个单元格值是否已经在当前的行 / 列 / 子数独中出现过:如果出现重复,返回 false。
如果没有,则保留此值以进行进一步跟踪。
如何枚举子数独?
可以使用 box_index = (row / 3) * 3 + columns / 3,其中 / 是整数除法。
如何确保行 / 列 / 子数独中没有重复项?
可以利用 value -> count 哈希映射来跟踪所有已经遇到的值。
python代码
class Solution(object):
def isValidSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: bool
"""
rows=[{}for i in range(9)]
columns=[{} for i in range(9)]
boxes = [{}for i in range(9)]
for i in range(9):
for j in range(9):
num = board[i][j]
if num!='.':
num = int(num)
box_index=(i//3)*3+j//3
rows[i][num] = rows[i].get(num, 0) + 1
columns[j][num] = columns[j].get(num, 0) + 1
boxes[box_index][num] = boxes[box_index].get(num, 0) + 1
#查重
if rows[i][num] > 1 or columns[j][num] > 1 or boxes[box_index][num] > 1:
return False
return True
37. 解数独
题目描述
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
1:数字 1-9 在每一行只能出现一次。
2:数字 1-9 在每一列只能出现一次。
3:数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。空白格用 ‘.’ 表示。
一个数独:
提示
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
思路
(递归回溯):
1.首先预处理出 col、row 和 squ 数组。
2.从 (0,0) 位置开始尝试并递归。遇到 . 时,枚举可以填充的数字,然后判重并加入 col、row 和 squ 数组中。
3.如果成功到达结尾,则返回 true,告知递归可以终止。
c++版代码
class Solution {
public:
bool row[9][9], col[9][9], cell[3][3][9];
void solveSudoku(vector>& board) {
memset(row, false, sizeof row);
memset(col, false, sizeof col);
memset(cell, false, sizeof cell);
for(int i = 0; i < 9; i++)
for(int j = 0; j < 9; j++)
if(board[i][j] != '.'){
int t = board[i][j] - '1';
row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;
}
dfs(board, 0, 0);
}
bool dfs(vector>& board, int x, int y){
if(y == 9) x += 1, y = 0;
if(x == 9) return true;
if(board[x][y] != '.') return dfs(board, x, y + 1);
for(int i = 0; i < 9; i++){
if(!row[x][i] && !col[y][i] && !cell[x / 3][y / 3][i]){
board[x][y] = i + '1';
row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = true;
if(dfs(board, x, y + 1)) return true;
board[x][y] = '.';
row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = false;
}
}
return false;
}
};
python代码
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
row = [[False for _ in range(9)] for _ in range(9)]
col = [[False for _ in range(9)] for _ in range(9)]
cell = [[False for _ in range(9)] for _ in range(9)]
for i in range(9):
for j in range(9):
if board[i][j] != '.':
t = int(board[i][j]) - 1
row[i][t] = col[j][t] = cell[i // 3 * 3 + j // 3][t] = True;
def dfs(board, x, y):
if y == 9:
x += 1; y = 0
if x == 9: return True
if board[x][y] != '.': return dfs(board, x, y + 1)
for i in range(9):
if not row[x][i] and not col[y][i] and not cell[x // 3 * 3 + y // 3][i]:
board[x][y] = str(i + 1)
row[x][i] = col[y][i] = cell[x // 3 * 3 + y // 3][i] = True
if(dfs(board, x, y + 1)): return True
board[x][y] = '.'
row[x][i] = col[y][i] = cell[x // 3 * 3 + y // 3][i] = False
return False
dfs(board, 0, 0)
38. 外观数列
题目描述
给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。
注意:整数序列中的每一项将表示为一个字符串。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:
第一项是数字 1
描述前一项,这个数是 1 即 “一个 1 ”,记作 11
描述前一项,这个数是 11 即 “两个 1 ” ,记作 21
描述前一项,这个数是 21 即 “一个 2 一个 1 ” ,记作 1211
描述前一项,这个数是 1211 即 “一个 1 一个 2 两个 1 ” ,记作 111221
示例 1:
输入: 1
输出: “1”
解释:这是一个基本样例。
示例 2:
输入: 4 输出: “1211” 解释:当 n = 3 时,序列是 “21”,其中我们有 “2” 和 “1” 两组,“2” 可以读作
“12”,也就是出现频次 = 1 而 值 = 2;类似 “1” 可以读作 “11”。所以答案是 “12” 和 “11” 组合在一起,也就是
“1211”。
思路
(模拟) O(n2)
直接按照从 2 到 n 的顺序生成字符串,即每次找连续相同的数字段,合并。
c++代码
class Solution {
public:
string countAndSay(int n) {
string s = "1";
for(int i=0;i
{
string t;
for(int j = 0;j
{
int k = j+1;
while(k
t += to_string(k-j)+s[j];
j = k;
}
s = t;
}
return s;
}
};
python代码
class Solution:
def countAndSay(self, n: int) -> str:
s = '1'
for i in range(n - 1):
j = 0
t = ""
while j < len(s):
k = j + 1
while k < len(s) and s[k] == s[j]: k += 1
t += str(k - j) + s[j]
j = k
s = t
return s
39. 组合总和
题目描述
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target
的组合。candidates 中的数字可以无限制重复被选取。
说明: 所有数字(包括 target)都是正整数。 解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ]
提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate中的每个元素都是独一无二的。 1 <= target <= 500
思路
(递归枚举)
在每一层搜索中,枚举这个数字添加几次。 搜索的终止条件是层数超过的数组的长度或者当前数字组合等于目标值。
剪枝:可以先将数组从小到大排序,搜索中如果 sum != target 并且 sum+candidates[i] > target,
则可以直接终止之后的递归,因为之后的数字都会比 candidates[i] 大,不会再产生答案。
c++版
class Solution {
public:
vector> ans;
vector path;
vector> combinationSum(vector& candidates, int target) {
dfs(candidates, 0, target);
return ans;
}
void dfs(vector& candidates, int cur, int target){
if(target == 0){
ans.push_back(path);
return ;
}
if(target < 0) return ;
if(cur == candidates.size()) return ;
for(int i = 0; i * candidates[cur] <= target; i++){
dfs(candidates, cur + 1, target - i * candidates[cur]);
path.push_back(candidates[cur]);
}
for(int i = 0; i * candidates[cur] <= target; i++)
path.pop_back();
}
};
python版
class Solution(object):
def combinationSum(self, candidates, target):
ans = []
path = []
def dfs(c, cur, target):
if target == 0:
ans.append(path[:])
return
if target < 0: return
if cur == len(c): return
i = 0
while(i * c[cur] <= target):
dfs(c, cur + 1, target - i * c[cur])
path.append(c[cur])
i += 1
i = 0
while(i * c[cur] <= target):
path.pop()
i += 1
dfs(candidates, 0, target)
return ans
"""
:type candidates: List[int]
:type target: int
:rtype: List[List[int]]
"""
40. 组合总和 II
题目描述
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
思路:
递归搜索: 排序,对于每层搜索,判断当前数字重复的次数,保证搜索下一层的数字和当前数字不一样
c++版
class Solution {
public:
vector>ans;
vectorpath;
vector> combinationSum2(vector& candidates, int target) {
sort(candidates.begin(),candidates.end());
dfs(candidates,0,target);
return ans;
}
void dfs(vectorcandidates,int u ,int target)
{
if(target==0)
{
ans.push_back(path);
return;
}
if (u == candidates.size()) return;
int k = u+1;
while(k
int cnt = k-u;
for(int i =0;i*candidates[u]<=target&&i<=cnt;i++)
{
dfs(candidates,k,target-i*candidates[u]);
path.push_back(candidates[u]);
}
for(int i = 0; candidates[u] * i <= target && i <= cnt; i ++ )
{
path.pop_back();
}
}
};
python版
class Solution:
def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]:
ans = []
path = []
candidates.sort()
def dfs(c, cur, target):
if target == 0:
ans.append(path[:])
return
if cur == len(c): return
if target < 0: return
k = cur + 1
while k < len(c) and c[k] == c[cur]: k += 1
cnt = k - cur
i = 0
while i <= cnt and i * c[cur] <= target:
dfs(c, k, target - i * c[cur])
path.append(c[cur])
i += 1
i = 0
while i <= cnt and i * c[cur] <= target:
path.pop()
i += 1
dfs(candidates, 0, target)
return ans