回溯法:实现
睡不醒的鲤鱼 2021-10-10 每日一题 LeetCode
# 0017. 电话号码的字母组合 (opens new window)
题目
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
1
2
2
示例 2:
输入:digits = ""
输出:[]
1
2
2
示例 3:
输入:digits = "2"
输出:["a","b","c"]
1
2
2
提示:
0 <= digits.length <= 4
digits[i]
是范围['2', '9']
的一个数字。
首先存储每个数字对应的所有可能的字母,然后进行回溯操作。回溯过程中维护一个字符串,表示已有的字母排列,并记录当前回溯位置。每次尝试对应位置数字的所有字母,即可得到完整排列。
代码
# 0022. 括号生成 (opens new window)
题目
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
有效括号组合需满足:左括号必须以正确的顺序闭合。
一个合法的括号序列需要满足以下两个条件:
- 任意前缀中左括号的数量 右括号的数量;
- 左右括号数量相等。
因此只要在回溯的同时,记录当前状态已使用的左右括号数,根据使用情况决定下一步状态即可。
代码
class Solution {
vector<string> ans;
public:
vector<string> generateParenthesis(int n) {
dfs(0, 0, n, "");
return ans;
}
void dfs(int lc, int rc, int n, string seq) {
if (lc == n && rc == n) {
ans.push_back(seq);
return;
}
if (lc < n) dfs(lc + 1, rc, n, seq + "(");
if (rc < n && lc > rc) dfs(lc, rc + 1, n, seq + ")");
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 0037. 解数独 (opens new window)
题目
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。
使用布尔数组记录行、列、3x3 宫格内的数字是否存在,每次尝试对应位置所有可能的数字,遍历到最后一个位置即可得到正确答案。
代码
class Solution {
bool rows[9][9], cols[9][9], cells[3][3][9];
public:
void solveSudoku(vector<vector<char>>& board) {
memset(rows, 0, sizeof(rows));
memset(cols, 0, sizeof(cols));
memset(cells, 0, sizeof(cells));
// 将已有数字初始化进数组
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (board[i][j] == '.') continue;
int t = board[i][j] - '1';
rows[i][t] = cols[j][t] = cells[i / 3][j / 3][t] = true;
}
}
dfs(board, 0, 0);
}
bool dfs(vector<vector<char>>& board, int x, int y) {
// 到达行末、切换至下一行
if (y == 9) x++, 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 (rows[x][i] || cols[y][i] || cells[x / 3][y / 3][i]) continue;
rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = true;
board[x][y] = '1' + i;
if (dfs(board, x, y + 1)) return true;
board[x][y] = '.';
rows[x][i] = cols[y][i] = cells[x / 3][y / 3][i] = false;
}
return false;
}
};
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
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
# 0140. 单词拆分 II (opens new window)
题目
给定一个字符串 s 和一个字符串字典 wordDict ,在字符串 s 中增加空格来构建一个句子,使得句子中所有的单词都在词典中。以任意顺序 返回所有这些可能的句子。
注意:词典中的同一个单词可能在分段中被重复使用多次。
DFS 枚举字典中出现的单词。
代码
class Solution {
unordered_set<string> st;
vector<string> ans;
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
st = unordered_set<string>(wordDict.begin(), wordDict.end());
dfs(s, 0, "");
return ans;
}
void dfs(string &s, int idx, string cur) {
if (idx == s.length()) {
ans.push_back(cur);
return;
}
for (int i = idx; i < s.length(); i++) {
string word = s.substr(idx, i - idx + 1);
if (st.count(word)) {
dfs(s, i + 1, !cur.empty() ? cur + " " + word : word);
}
}
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 0282. 给表达式添加运算符 (opens new window)
题目
解析
代码
# 0320. 列举单词的全部缩写 (opens new window)
题目
解析
代码
# 0401. 二进制手表 (opens new window)
题目
解析
代码
# 0465. 最优账单平衡 (opens new window)
题目
解析
代码
# 0473. 火柴拼正方形 (opens new window)
题目
解析
代码
# 0491. 递增子序列 (opens new window)
题目
解析
代码
# 0526. 优美的排列 (opens new window)
题目
解析
代码
# 0582. 杀掉进程 (opens new window)
题目
解析
代码