回溯算法
代码模板
思路就是通过横向for循环,纵向递归的形式(递归里面嵌for循环套)
代码模板如下
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择的列表); // 递归
回溯,撤销处理结果
}
}
LeetCode 491.递增子序列
题目链接
思路
树枝和数层的去重特别注意:
#对树层去重
if nums[i] in used:
continue
#对树枝去重
if i>0 and nums[i] < nums[i-1]:
continue
注意uesd与之前的区别,这里是存本轮树层有过的数,之前的uesd存的path出现过的数
class Solution:
def findSubsequences(self, nums: List[int]) -> List[List[int]]:
path = []
result = []
def backtracking(nums, start_index):
# 收集结果,同78.子集,仍要置于终止条件之前
if len(path) > 1:
# 本题要求所有的节点
result.append(path[:])
#return 不能写return 不然不会执行后面的内容
# Base Case(可忽略)
if start_index == len(nums):
return
# 单层递归逻辑
# 深度遍历中每一层都会有一个全新的usage_list用于记录本层元素是否重复使用
used = []
# 同层横向遍历
for i in range(start_index, len(nums)):
# 若当前元素值小于前一个时(非递增)或者曾用过,跳入下一循环
if (path and nums[i] < path[-1]) or nums[i] in used:
continue
used.append(nums[i])
path.append(nums[i])
backtracking(nums, i+1)
path.pop()
backtracking(nums, 0)
return result
LeetCode 46.全排列
题目链接
思路
还是比较模板的,跟组合的区别就是不要写那个startIndex,只要写used做标记,注意pop的时候要把标记改为False
class Solution:
def permute(self, nums: List[int]) -> List[List[int]]:
path = []
result = []
used = [False] * len(nums)
def backtracking(nums, uesd):
if len(path) == len(nums):
result.append(path[:])
return
for i in range(0, len(nums)):
if used[i] == True: continue
path.append(nums[i])
used[i] = True
backtracking(nums, used)
used[i] = False
path.pop()
backtracking(nums, used)
return result
LeetCode 47.全排列 II
题目链接
思路
跟组合Ⅱ一致,跟上题的区别在于要对nums排序,continue掉与上一个元素相同并且上一个为False的元素
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
nums = sorted(nums)
path = []
result = []
used = [False] * len(nums)
def backtracking(nums, uesd):
if len(path) == len(nums):
result.append(path[:])
return
for i in range(0, len(nums)):
if i>0 and nums[i] == nums[i-1] and used[i-1] == False: continue
if used[i] == True: continue
path.append(nums[i])
used[i] = True
backtracking(nums, used)
used[i] = False
path.pop()
backtracking(nums, used)
return result