如果要解决一个回溯法的问题,通常要确定三个元素:
1、选择。对于每个特定的解,肯定是由一步步构建而来的,而每一步怎么构建,肯定都是有限个选择,要怎么选择,这个要知道;同时,在编程时候要定下,优先或合法的每一步选择的顺序,一般是通过多个if或者for循环来排列。
2、条件。对于每个特定的解的某一步,他必然要符合某个解要求符合的条件,如果不符合条件,就要回溯,其实回溯也就是递归调用的返回。
3、结束。当到达一个特定结束条件时候,就认为这个一步步构建的解是符合要求的解了。把解存下来或者打印出来。对于这一步来说,有时候也可以另外写一个issolution函数来进行判断。注意,当到达第三步后,有时候还需要构建一个数据结构,把符合要求的解存起来,便于当得到所有解后,把解空间输出来。这个数据结构必须是全局的,作为参数之一传递给递归函数。
对于回溯法来说,每次递归调用,很重要的一点是把每次递归的不同信息传递给递归调用的函数。而这里最重要的要传递给递归调用函数的信息,就是把上一步做过的某些事情的这个选择排除,避免重复和无限递归。另外还有一个信息必须传递给递归函数,就是进行了每一步选择后,暂时还没构成完整的解,这个时候前面所有选择的汇总也要传递进去。而且一般情况下,都是能从传递给递归函数的参数处,得到结束条件的。
递归函数的参数的选择,要遵循四个原则:
1、必须要有一个临时变量(可以就直接传递一个字面量或者常量进去)传递不完整的解,因为每一步选择后,暂时还没构成完整的解,这个时候这个选择的不完整解,也要想办法传递给递归函数。也就是,把每次递归的不同情况传递给递归调用的函数。
2、可以有一个全局变量,用来存储完整的每个解,一般是个集合容器(也不一定要有这样一个变量,因为每次符合结束条件,不完整解就是完整解了,直接打印即可)。
3、最重要的一点,一定要在参数设计中,可以得到结束条件。一个选择是可以传递一个量n,也许是数组的长度,也许是数量,等等。
4、要保证递归函数返回后,状态可以恢复到递归前,以此达到真正回溯。
本解法采用回溯算法实现,回溯算法的基本形式是“递归+循环”,正因为循环中嵌套着递归,递归中包含循环,这才使得回溯比一般的递归和单纯的循环更难理解,其实我们熟悉了它的基本形式,就会觉得这样的算法难度也不是很大。原数组中的每个元素有两种状态:存在和不存在。
① 外层循环逐一往中间集合 temp 中加入元素 nums[i],使这个元素处于存在状态
② 开始递归,递归中携带加入新元素的 temp,并且下一次循环的起始是 i 元素的下一个,因而递归中更新 i 值为 i + 1
③ 将这个从中间集合 temp 中移除,使该元素处于不存在状态,恢复状态
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
ans=[] #全局变量,储存完整的解
def dfs(lst,nums,pos):#lst 临时变量,储存不完整的解
ans.append(lst[:])
for i in range(pos,len(nums)):# 结束条件当pos==len(nums)时
lst.append(nums[i])
dfs(lst,nums,i+1)
lst.pop() #回溯
dfs([],nums,0)
return ans
数组 [1,2,3] 的子集也就是其中的三个元素取与不取的组合。把它想象为二进制的三个 bit 位 1 1 1,那么从 0 0 0 到 1 1 1 的 8 个数,就构成了所有子集的选取情况。比如 0 0 1 表示取第1个元素,0 1 1 表示取前两个元素。
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
results=[]
subsets_size=2**len(nums)
n=0
while n< subsets_size:
subset=[]
i=0
bits=n
while bits>0:
if bits&1 ==1:
subset.append(nums[i])
i+=1
bits=bits>>1
n+=1
results.append(subset)
return results
直接从后遍历,遇到一个数就把所有子集加上该数组成新的子集,遍历完毕即是所有子集
class Solution:
def subsets(self, nums: List[int]) -> List[List[int]]:
if(len(nums)==0):
return [[]]
if(len(nums)==1):
return [[],nums]
ans=self.subsets(nums[:-1])
ans+= [i+[nums[-1]] for i in ans]
return ans
① 最外层循环逐一从 nums 数组中取出每个元素 num
② 内层循环从原来的结果集中取出每个中间结果集,并向每个中间结果集中添加该 num 元素
③往每个中间结果集中加入 num
④将新的中间结果集加入结果集中
class Solution(object):
def subsets(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = [[]]
for num in nums :
for temp in res[:] :# 注意不能是 res,res[:]创建了res 的副本
x = temp[:]
x.append(num)
res.append(x)
return res