子集
1.解法
子集问题也是回溯问题中最经典的一种。
我们先把树图画出来:
由图可以看出,如果想得到一个集合的子集,其实就是从一个集合中挑选元素,挑选方法为:
- 首先选出第一个元素,然后再把这个元素之后的所有元素作为新集合思考同样的挑选问题
- 之后选出第二个元素,然后再把这个元素之后的所有元素作为新集合思考同样的挑选问题
… - 以此类推,直到新集合为空
由上可知他们的递归逻辑
我们根据回溯三部曲来写代码:
-
确定函数头和一些辅助函数:
result = [] # 存放最终结果 path = [] # 存放当前结果 def backtracking(nums,startindex): # startindex用来确定新集合的开始位置
-
确定递归出口:
从图中可以看出,当集合为空的时候就要返回,那么什么时候集合为空呢? if startindex>=len(nums): return 注意这里不是当集合为空的时候才把path存入result中,这里也是子集问题区别于组合问题的关键
-
确定逻辑部分:
path1 = path.copy() result.append(path1) for i in range(startindex,len(nums)): path.append(nums[i]) backtracking(nums,i+1) path.pop() 每次挑选一个数值,然后再从新集合中重复同样的操作,注意只要挑选到了一个数值就可以存入result中,没有其他条件。
完整代码为:
def subsets(nums):
result = []
path = []
def backtracking(nums,startindex):
path1 = path.copy() # 注意这里要写到出口前面,一是可以得到空集,二是可以得到本身
result.append(path1)
if startindex>=len(nums): # 其实这个出口可以不写,因为startindex>=len(nums)满足时,下面的for循环也要返回了。
return
for i in range(startindex,len(nums)):
path.append(nums[i])
backtracking(nums,i+1)
path.pop()
backtracking(nums,0)
return result
2.总结
算法
子集问题的模板为:
一般不需要递归出口,因为for循环的判断就可以进行退出。
def subsets(nums):
result = []
path = []
def backtracking(nums,startindex):
path1 = path.copy()
result.append(path1)
for i in range(startindex,len(nums)):
path.append(nums[i])
backtracking(nums,i+1)
path.pop()
backtracking(nums,0)
return result