目录
- 一、子集型回溯
-
- [1.1 电话号码](#1.1 电话号码)
- [1.2 子集](#1.2 子集)
- [1.3 分割回文串](#1.3 分割回文串)
- 二、组合型与剪枝
-
- [2.1 组合](#2.1 组合)
- [2.2 组合总和 III](#2.2 组合总和 III)
- [2.3 括号生成](#2.3 括号生成)
- 三、排列型
-
- [3.1 全排列](#3.1 全排列)
- [3.2 N 皇后](#3.2 N 皇后)
- [3.3 N 皇后 II](#3.3 N 皇后 II)
一、子集型回溯
1.1 电话号码
python
MAPPING = "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"
class Solution:
def letterCombinations(self, digits: str) -> List[str]:
n = len(digits)
if n == 0:
return []
ans = []
path = [''] * n
def dfs(i: int)->None:
if i == n:
ans.append(''.join(path))
return
for c in MAPPING[int(digits[i])]:
path[i] = c
dfs(i + 1)
dfs(0)
return ans
cpp
class Solution {
private:
string MAPPING[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public:
vector<string> letterCombinations(string digits) {
int n = digits.size();
if (!n) return {};
vector<string> ans;
string path(n, 0);
function<void(int)> dfs = [&](int i ) {
if (i == n) {
ans.emplace_back(path);
return;
}
for (char c: MAPPING[digits[i] - '0']) {
path[i] = c;
dfs(i + 1);
}
};
dfs(0);
return ans;
}
};
- 时间复杂度: O ( n 4 n ) O(n4^n) O(n4n)
- 空间复杂度: O ( n ) O(n) O(n)
1.2 子集
解法一:选或者不选
python
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans = []
path = []
n = len(nums)
def dfs(i: int)->None:
if i == n:
ans.append(path.copy())
return
dfs(i + 1)
path.append(nums[i])
dfs(i + 1)
path.pop()
dfs(0)
return ans
cpp
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> path;
int n = nums.size();
function<void(int)> dfs=[&](int i) {
if (i == n) {
ans.emplace_back(path);
return;
}
// 不选
dfs(i + 1);
// 选择
path.push_back(nums[i]);
dfs(i + 1);
path.pop_back();
};
dfs(0);
return ans;
}
};
解法二:选择哪个数
python
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans, path = [], []
n = len(nums)
def dfs(i: int)->None:
ans.append(path.copy())
if i == n:
return
for j in range(i, n):
path.append(nums[j])
dfs(j + 1)
path.pop()
dfs(0)
return ans
cpp
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> ans;
vector<int> path;
int n = nums.size();
function<void(int)> dfs = [&] (int i) {
ans.emplace_back(path);
if (i == n) return;
for (int j = i; j < n; j ++ ) {
path.push_back(nums[j]);
dfs(j + 1);
path.pop_back();
}
};
dfs(0);
return ans;
}
};
1.3 分割回文串
python
class Solution:
def partition(self, s: str) -> List[List[str]]:
ans, path = [], []
n = len(s)
def dfs(i: int) -> None:
if i == n:
ans.append(path.copy())
return
for j in range(i, n):
t = s[i : j + 1]
if t == t[::-1]:
path.append(t)
dfs(j + 1)
path.pop()
dfs(0)
return ans
cpp
class Solution {
bool isPalindrome(string &s, int left, int right) {
while (left < right)
if (s[left ++ ] != s[right -- ])
return false;
return true;
}
public:
vector<vector<string>> partition(string s) {
vector<vector<string>> ans;
vector<string> path;
int n = s.length();
function<void(int)> dfs = [&](int i) {
if (i == n) {
ans.emplace_back(path);
return;
}
for (int j = i; j < n; j ++ )
if (isPalindrome(s, i, j)) {
path.push_back(s.substr(i, j - i + 1));
dfs(j + 1);
path.pop_back();
}
};
dfs(0);
return ans;
}
};
二、组合型与剪枝
2.1 组合
解法一:选或者不选
python
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
ans, path = [], []
def dfs(i: int)->None:
d = k -len(path)
if d == 0:
ans.append(path.copy())
return
# 不选i
if i > d: dfs(i - 1)
# 选择i
path.append(i)
dfs(i - 1)
path.pop()
dfs(n)
return ans
cpp
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> path;
function<void(int)> dfs = [&](int i) {
int d = k - path.size();
if (!d) {
ans.emplace_back(path);
return;
}
if (i > d) dfs(i - 1);
path.push_back(i);
dfs(i - 1);
path.pop_back();
};
dfs(n);
return ans;
}
};
解法二:枚举下一个数选择哪个
python
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
ans, path = [], []
def dfs(i: int)->None:
d = k - len(path) # 还有选择d个数
if d == 0:
ans.append(path.copy())
return
for j in range(i, d - 1, -1):
path.append(j)
dfs(j - 1)
path.pop()
dfs(n)
return ans
cpp
class Solution {
public:
vector<vector<int>> combine(int n, int k) {
vector<vector<int>> ans;
vector<int> path;
function<void(int)> dfs = [&](int i) {
int d = k - path.size();
if (!d) {
ans.emplace_back(path);
return;
}
for (int j = i; j >= d; j -- ) {
path.push_back(j);
dfs(j - 1);
path.pop_back();
}
};
dfs(n);
return ans;
}
};
2.2 组合总和 III
解法一:枚举下一个属选择哪个
python
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
ans, path = [], []
def dfs(i: int, t: int)->None:
"""
i 表示枚举到数(1-9)
t 表示剩余的总和
"""
d = k - len(path)
# 剪枝1:剩余总和小于0
# 剪枝2:等差求和任然小于t
if t < 0 or t > (i + i - d + 1) * d // 2:
return
if d == 0:
ans.append(path.copy())
return
for j in range(i, d - 1, -1):
path.append(j)
dfs(j - 1, t - j)
path.pop()
dfs(9, n)
return ans
cpp
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> ans;
vector<int> path;
function<void(int, int)> dfs = [&](int i, int t) {
int d = k - path.size();
if (t < 0 || t > (i + i - d + 1) * d / 2) return;
if (!d) {
ans.emplace_back(path);
return;
}
for (int j = i; j >= d; j -- ) {
path.push_back(j);
dfs(j - 1, t - j);
path.pop_back();
}
};
dfs(9, n);
return ans;
}
};
解法二:选或者不选
python
class Solution:
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
ans, path = [], []
def dfs(i: int, t: int)->None:
d = k - len(path)
if t < 0 or t > (i + i - d + 1) * d // 2:
return
if d == 0:
ans.append(path.copy())
return
if (i > d):
dfs(i - 1, t)
path.append(i)
dfs(i - 1, t - i)
path.pop()
dfs(9, n)
return ans
cpp
class Solution {
public:
vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> ans;
vector<int> path;
function<void(int, int)> dfs = [&] (int i, int t) {
int d = k - path.size();
if (t < 0 || t > (i + i - d + 1) * d / 2) return;
if (!d) {
ans.emplace_back(path);
return;
}
if (i > d) dfs(i - 1, t);
path.push_back(i);
dfs(i - 1, t - i);
path.pop_back();
};
dfs(9, n);
return ans;
}
};
2.3 括号生成
解法一:枚举填左括号还是右括号
python
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
m = n * 2
ans = []
path = [''] * m
def dfs(i: int, open: int)->None:
"""
i: 记录当前枚举到的位置
open: 记录左括号个数
"""
if i == m:
ans.append(''.join(path))
return
if open < n: # 还可以填写左括号
path[i] = '('
dfs(i + 1, open + 1)
if i - open < open: # 如果括号匹配, i-open == open
path[i] = ')'
dfs(i + 1, open)
dfs(0, 0)
return ans
cpp
class Solution {
public:
vector<string> generateParenthesis(int n) {
int m = n * 2;
vector<string> ans;
string path(m, 0);
function<void(int, int)> dfs = [&](int i, int open) {
if (i == m) {
ans.emplace_back(path);
return;
}
if (open < n) {
path[i] = '(';
dfs(i + 1, open + 1);
}
if (i - open < open) {
path[i] = ')';
dfs(i + 1, open);
}
};
dfs(0, 0);
return ans;
}
};
解法二:枚举下一个左括号的位置
python
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans, path = [], []
# balance = 左括号个数 - 右括号个数
def dfs(i: int, balance: int)->None:
if len(path) == n:
s = [')'] * (n * 2)
for j in path:
s[j] = '('
ans.append(''.join(s))
return
# 可以填写 0 到 balance 个右括号
for close in range(balance + 1): # 填 close 个右括号
path.append(i + close) # 填 1 个左括号
dfs(i + close + 1, balance - close + 1)
path.pop()
dfs(0, 0)
return ans
cpp
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> ans;
vector<int> path;
function<void(int, int)> dfs=[&](int i, int balance) {
if (path.size() == n) {
string s(n * 2, ')');
for (int j: path) s[j] = '(';
ans.emplace_back(s);
return;
}
for (int close = 0; close <= balance; close ++ ) {
path.push_back(i + close);
dfs(i + close + 1, balance - close + 1);
path.pop_back();
}
};
dfs(0, 0);
return ans;
}
};
三、排列型
3.1 全排列
python
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans, path = [], [0] * n
on_path = [False] * n # 判断是否该数是否被使用
def dfs(i: int)->None:
if i == n:
ans.append(path.copy())
return
for j, on in enumerate(on_path):
if not on:
path[i] = nums[j]
on_path[j] = True
dfs(i + 1)
on_path[j] = False
dfs(0)
return ans
cpp
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> ans;
vector<int> path(n), on_path(n);
function<void(int)> dfs = [&](int i) {
if (i == n) {
ans.emplace_back(path);
return;
}
for (int j = 0; j < n; j ++ )
if (!on_path[j]) {
path[i] = nums[j];
on_path[j] = true;
dfs(i + 1);
on_path[j] = false;
}
};
dfs(0);
return ans;
}
};
3.2 N 皇后
python
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
m = n * 2 - 1
ans = []
col = [0] * n
on_path, diag1, diag2 = [False] * n, [False] * m, [False] * m
def dfs(r: int)->None:
if r == n:
ans.append(['.' * c + 'Q' + '.' * (n - 1 - c) for c in col])
return
for c, on in enumerate(on_path):
if not on and not diag1[r + c] and not diag2[r - c]:
col[r] = c
on_path[c] = diag1[r + c] = diag2[r - c] = True
dfs(r + 1)
on_path[c] = diag1[r + c] = diag2[r - c] = False
dfs(0)
return ans
cpp
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector<vector<string>> ans;
vector<int> col(n), on_path(n), diag1(n * 2 - 1), diag2(n * 2 - 1);
function<void(int)> dfs=[&](int r) {
if (r == n) {
vector<string> board(n);
for (int i = 0; i < n; i ++ )
board[i] = string(col[i], '.') + 'Q' + string(n - 1 - col[i], '.');
ans.emplace_back(board);
return;
}
for (int c = 0; c < n; c ++ ) {
int rc = r - c + n - 1;
if (!on_path[c] && !diag1[r + c] && !diag2[rc]) {
col[r] = c;
on_path[c] = diag1[r + c] = diag2[rc] = true;
dfs(r + 1);
on_path[c] = diag1[r + c] = diag2[rc] = false;
}
}
};
dfs(0);
return ans;
}
};
3.3 N 皇后 II
python
class Solution:
def totalNQueens(self, n: int) -> int:
m = n * 2 - 1
ans = 0
on_path, diag1, diag2 = [False] * n, [False] * m, [False] * m
def dfs(r: int)->None:
if r == n:
nonlocal ans
ans += 1
return
for c, on in enumerate(on_path):
if not on and not diag1[r + c] and not diag2[r - c]:
on_path[c] = diag1[r + c] = diag2[r - c] = True
dfs(r + 1)
on_path[c] = diag1[r + c] = diag2[r - c] = False
dfs(0)
return ans
cpp
class Solution {
public:
int totalNQueens(int n) {
int ans = 0;
vector<int> on_path(n), diag1(n * 2 - 1), diag2(n * 2 - 1);
function<void(int)> dfs=[&] (int r) {
if (r == n) {
ans ++ ;
return;
}
for (int c = 0; c < n; c ++ ) {
int rc = r - c + n - 1;
if (!on_path[c] && !diag1[r + c] && !diag2[rc]) {
on_path[c] = diag1[r + c] = diag2[rc] = true;
dfs(r + 1);
on_path[c] = diag1[r + c] = diag2[rc] = false;
}
}
};
dfs(0);
return ans;
}
};