回溯算法套路②组合型回溯+剪枝【基础算法精讲 15】_哔哩哔哩_bilibili
77. 组合
方法一:枚举下一个数选哪个
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
算法的核心部分是
dfs
函数。它有一个参数i
,表示下一个将要添加到path
中的数字的起始位置(因为答案需要是递增序列)。同时,还计算了变量d
,表示还需要选取几个数字才能完成一个长度为k
的序列。如果
d == 0
,说明当前序列已经满足要求,我们将其加入到ans
中,并返回。否则,我们从
i
开始往后遍历可能的数字,对于每个数字j
,都尝试将其添加到path
中,并递归调用dfs
函数,以便继续往后选择数字。递归调用完成后,需要将path
中的最后一个元素弹出,以便尝试其他可能的数字。最终,当
dfs(n)
调用完成时,我们得到了所有可能的长度为k
的序列,将其返回即可。对于path.copy()是因为如果我们后续修改了
path
,那么ans
中保存的所有序列也会被修改,这可能导致我们得到错误的答案。为了避免这种情况,我们可以使用copy()
方法创建path
的一个浅拷贝(即仅复制一层),并将其添加到ans
中。这样,即使我们修改了path
,ans
保存的所有序列也不会受到影响。
方法二:选或不选
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
# 不选 i
if i > d: dfs(i - 1)
# 选 i
path.append(i)
dfs(i - 1)
path.pop()
dfs(n)
return ans
216. 组合总和 III
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
方法一:枚举下一个数选哪个
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) # 还要选 d 个数
if t < 0 or t > (i * 2 - 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
方法二:选或不选
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) # 还要选 d 个数
if t < 0 or t > (i * 2 - d + 1) * d // 2: # 剪枝
return
if d == 0:
ans.append(path.copy())
return
# 不选 i
if i > d: dfs(i - 1, t)
# 选 i
path.append(i)
dfs(i - 1, t - i)
path.pop()
dfs(9, n)
return ans
22. 括号生成
方法一:枚举选左括号还是右括号
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
m = n * 2
ans = []
path = [''] * m
def dfs(i: int, open: int) -> None:
if i == m:
ans.append(''.join(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
方法二:枚举下一个左括号的位置
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