一、491.递增子序列
题目链接:491. 非递减子序列 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——491.递增子序列
视频讲解:回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列_哔哩哔哩_bilibili
思路:不能进行排序!!!采用树层去重,收集节点结果,仅仅要求数量大于2。
树形结构:
class Solution:
# 1. 确定递归函数的参数和返回值
def backtracking(self, nums, path, result, startindex):
# 2. 确定递归终止条件
if len(path) > 1:
result.append(path[:])
# 3. 确定单层递归逻辑
used = set() # 用set记录用过的元素,如果后序再set中出现则不用,去重
for i in range(startindex, len(nums)):
# path不能为空,且nums[i]小于path中最右边元素;同时元素在之前set中没出现过
if (path and nums[i] < path[-1]) or nums[i] in used:
continue # 如果重复后面可能有元素继续使用,而不是break
used.add(nums[i]) # 记录用过的元素
path.append(nums[i])
self.backtracking(nums, path, result, i+1)
path.pop()
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
path = []
result = []
self.backtracking(nums, path, result, 0)
return result
二、46.全排列
题目链接:46. 全排列 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——46.全排列
视频讲解:组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列_哔哩哔哩_bilibili
Note:排列强调顺序。
树形结构:
class Solution:
# 1. 确定递归函数的参数和返回值
# used,标记那些用过,排序要求不重复使用
def backtracking(self, nums, path, result, used):
# 2. 确定递归终止条件,path大小和集合大小一样时候,可以收获结果
if len(path) == len(nums):
result.append(path[:])
return
# 3. 确定单层递归逻辑
for i in range(len(nums)):
# 如果用了,直接跳过元素,不重复取元素,所以continue
if used[i]:
continue
used[i] = True
path.append(nums[i])
self.backtracking(nums, path, result, used)
path.pop() # 回溯
used[i] = False # 回溯
def permute(self, nums: List[int]) -> List[List[int]]:
used = [False] * len(nums)
path = []
result = []
self.backtracking(nums, path, result, used)
return result
三、47.全排列 II
题目链接:47. 全排列 II - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——47.全排列 II
视频讲解:回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II_哔哩哔哩_bilibili
class Solution:
# 1. 确定递归函数的参数和返回值
def backtracking(self, nums, path, result, used):
# 2. 确定递归终止条件
if len(path) == len(nums):
result.append(path[:])
return
# 3. 确定单层递归逻辑
for i in range(len(nums)):
if used[i]:
continue
# 树层去重
if i > 0 and nums[i] == nums[i - 1] and used[i - 1] == False:
continue
used[i] = True
path.append(nums[i])
self.backtracking(nums, path, result, used)
path.pop()
used[i] = False
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
used = [False] * len(nums)
nums.sort()
path = []
result = []
self.backtracking(nums, path, result, used)
return result
四、332.重新安排行程(可跳过)
题目链接:332. 重新安排行程 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——332.重新安排行程
五、51.N皇后(适当跳过)
题目链接:51. N 皇后 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——51.N皇后
视频讲解:这就是传说中的N皇后? 回溯算法安排!| LeetCode:51.N皇后_哔哩哔哩_bilibili
树形结构:
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
result = [] # 存储最终结果的二维字符串数组
chessboard = ['.' * n for _ in range(n)] # 初始化棋盘
self.backtracking(n, 0, chessboard, result) # 回溯求解
return [[''.join(row) for row in solution] for solution in result] # 返回结果集
def backtracking(self, n: int, row: int, chessboard: List[str], result: List[List[str]]) -> None:
if row == n:
result.append(chessboard[:]) # 棋盘填满,将当前解加入结果集
return
for col in range(n):
if self.isValid(row, col, chessboard):
chessboard[row] = chessboard[row][:col] + 'Q' + chessboard[row][col+1:] # 放置皇后
self.backtracking(n, row + 1, chessboard, result) # 递归到下一行
chessboard[row] = chessboard[row][:col] + '.' + chessboard[row][col+1:] # 回溯,撤销当前位置的皇后
def isValid(self, row: int, col: int, chessboard: List[str]) -> bool:
# 检查列
for i in range(row):
if chessboard[i][col] == 'Q':
return False # 当前列已经存在皇后,不合法
# 检查 45 度角是否有皇后
i, j = row - 1, col - 1
while i >= 0 and j >= 0:
if chessboard[i][j] == 'Q':
return False # 左上方向已经存在皇后,不合法
i -= 1
j -= 1
# 检查 135 度角是否有皇后
i, j = row - 1, col + 1
while i >= 0 and j < len(chessboard):
if chessboard[i][j] == 'Q':
return False # 右上方向已经存在皇后,不合法
i -= 1
j += 1
return True # 当前位置合法
六、37.解数独(适当跳过)
题目链接:37. 解数独 - 力扣(LeetCode)
文章讲解:代码随想录 (programmercarl.com)——37.解数独
视频讲解:回溯算法二维递归?解数独不过如此!| LeetCode:37. 解数独_哔哩哔哩_bilibili