LeetCode题目总结——回溯
1. 找到所有组合
使用回溯法实现深度优先搜索,以找到满足某条件的所有结果并返回。
# cur: 当前正在尝试的组合
# ans: 当前已找到的满足条件的所有组合
# states: 用于判断条件是否满足的状态量
def backtrack(inputs, cur, ans, states):
# 结束条件
if states ...:
ans.append(cur)
return
# 搜索当前节点下的每条路径
for ...:
cur.append(...)
backtrack(inputs, cur, ans, states - ...)
cur.pop()
- 0017 - 电话号码的数字组合
- 0022 - 括号生成
- 0039 - 组合总和
- 0040 - 组合总和 II
使用一个数组记录元素剩余数量。 - 0046 - 全排列
通过依次交换当前位置元素与其后方各个元素的位置,来完成回溯状态控制,不需要维护occupied数组。 - 0047 - 全排列 II
必须维护occupied数组和cur数组,无法仅通过交换元素维护回溯状态。 - 0051 - N皇后
- 0052 - N皇后 II
除了维护一个常规的occupied数组外,还要维护两个对角线occupied数组。同时可以考虑使用位运算加速,对角线occupied数组可以使用左移右移实现。 - 0090 - 子集II
集合中存在重复元素,现对集合排序,再组合子集。重点注意如何跳过重复元素。
for (int i = p; i < nums.size(); ++i) {
// 重复元素跳过逻辑
if (i > p && nums[i] == nums[i - 1]) {
continue;
}
// 回溯逻辑
cur.push_back(nums[i]);
backtrack(res, nums, i + 1, cur);
cur.pop_back();
}
- 0093 - 复原IP地址
- 0113 - 路径总和 II
2. 深度优先搜索
- 0079 - 单词搜索
3. 找到所有组合——字典序法
- 0077 - 组合
字典序法,避免递归。 - 0078 - 子集
for (int mask = 0; mask < (1 << N); ++mask) {
// 添加一条结果
...
for (int i = 0; i < N; ++i) {
if (mask & (1 << i)) {
// 添加结果中的一个元素
...
}
}
}
4. 查找第k个元素
由于不需要所有的组合,因此可以通过运算快速定位第k个元素的位置,避免采用回溯从头开始遍历。
// 排列函数
size_t comb(int a, int b) {
if (b == 0) return 1;
size_t res = 1;
for (int i = a - b + 1; i <= a; ++i) {
res *= i;
}
for (int i = 1; i <= b; ++i) {
res /= i;
}
return res;
}
// 组合函数
size_t perm(int a, int b) {
if (b == 0) return 1;
size_t res = 1;
for (int i = a - b + 1; i <= a; ++i) {
res *= i;
}
return res;
}
- 0060 - 第k个排列
位置i的元素索引为:idx = [(k - 1) / (n - i)!]。 k的迭代公式为: k = (k - 1) % (n - i)! + 1
// 第k个排列函数
void perm_k(int a, int b, int k, vector<int>& res) {
vector<int> occupied(a, false);
// int div = perm(a, b);
for (int p = 0; p < b; ++p) {
// div /= a - p;
// int s = (k - 1) / div;
int s = (k - 1) / perm(a - p - 1, b - p - 1)
for (int i = 0; i < occupied.size(); ++i) {
if (!occupied[i]) {
if (s == 0) {
res.emplace_back(i);
occupied[i] = true;
break;
}
--s;
}
}
k = (k - 1) % div + 1;
}
}
- 第k个组合
// 第k个组合函数
void comb_k(int a, int b, int k, vector<int>& res) {
for (int p = 0; p < a; ++p) {
if (res.size() == b) return;
int t = comb(a - p - 1, b - res.size() - 1);
if (k > t) {
k -= t;
}
else {
res.emplace_back(p);
}
}
}