力扣一篇关于回溯的解析,后面给了很多例题:回溯解析及例题
知乎关于回溯的文章:知乎回溯
回溯能解决的问题:
组合、排列、切割、子集、棋盘问题、解数独
回溯法在结局问题的时候都可以抽象成一颗n叉树
回溯的模板:
函数无返回值,函数名backtrack()
res = [] # 定义全局变量保存最终结果
state = [] # 定义状态变量保存当前状态
p,q,r # 定义条件变量(一般条件变量就是题目直接给的参数)
def backtrack(状态,条件1,条件2,……):
if # 不满足合法条件(可以说是剪枝)
return
elif # 状态满足最终要求
res.append(state) # 加入结果
return
# 主要递归过程,一般是带有 循环体 或者 条件体
for # 满足执行条件,注意起点
if # 满足执行条件
backtrack(状态,条件1,条件2,……)
back(状态,条件1,条件2,……)
return res
关于排列的问题,回溯有着天然的优势,换句话说,对于排列的问题,第一个想到的就是回溯,
使用回溯来排列,难点就是去重的问题,去重有两种,一种是不允许有任何重复的元素(其本身没有重复的元素),还有一种就是排序前的本身有相同的元素,但是排列的时候不区分它们,比如 1,2l , 2 和1, 2, 2l 虽然两个2不相同,但是是属于一个相同的排序,要把这一部分剪枝掉
以下面这题为例:
力扣M47 全排列2
如果视两个不同的1 为不同的元素,那么代码为:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
n = len(nums)
ans = []
# not_in 就是还没有进入排列的数
# al_in就是已经排列过的数
def backtrack(not_in, al_in):
if not not_in:
ans.append(al_in)
for i in range(len(not_in)):
backtrack(not_in[:i]+not_in[i+1:], al_in+[not_in[i]])
backtrack(nums, [])
return ans
如果这两个2是一个,那就需要去重,去重的方法为先对nums进行排序,将相同的数全部放到一起,在for的时候,当当前的数==前一个数的时候直接continue,因为上一个2已经包括了我这个2的所有情况
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
ans = []
# sort是为了把所有相同的数放到一起
nums.sort()
def back(not_in, al_in):
# 如果没有进来的已经没有了(全部都进来了)
if not not_in:
ans.append(al_in)
return
else:
for i in range(len(not_in)):
# 区别就在这里,但是一定要先排序
if i>0 and not_in[i]==not_in[i-1]:
continue
else:
back(not_in[:i]+not_in[i+1:], al_in+[not_in[i]])
back(nums, [])
return ans