使用递归回溯生成集合的所有子集(Python)
问题描述
我试图理解回溯,但我陷入了这个问题,提示如下:
给定一组不同的整数,返回所有可能的子集。
示例输入:[1,2,3]
示例输出:[[], [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]]
以下是我的代码:
def subsets(nums):
res = []
backtrack(res, [], nums, 0)
return res
def backtrack(res, temp, nums, start):
# print(temp)
res.append(temp)
for i in range(start, len(nums)):
temp.append(nums[i])
backtrack(res, temp, nums, i + 1)
temp.pop() # Backtrack
当我返回res
时,我得到一个大小为2^(len(nums))
的空列表列表,它的大小是正确的,但数字不在那里。然而,在我执行之前打印temp
显示Temp正在携带正确的输出。
例如
res = [[], [], [], [], [], [], [], []]
打印报表:
[] [1] [1, 2] [1, 2, 3] [1, 3] [2] [2, 3] [3]
为什么更改没有应用到res
列表?
编辑1:
此解决方案有效,有什么区别?
def subsets(nums):
res = []
backtrack(res, [], nums, 0)
return res
def backtrack(res, temp, nums, start):
# print(temp)
res.append(temp)
for i in range(start, len(nums)):
backtrack(res, temp + [nums[i]], nums, i + 1)
解决方案
您正在将对同一列表对象的多个引用追加到res
。我们可以通过以下操作来演示这一点
result = subsets([1, 2, 3])
print([id(u) for u in result])
这将打印一个包含8个相同ID的列表。
因此,您对temp
所做的各种更改将"丢失",res
的最终内容将是对temp
的最终值的8个引用,在本例中为空列表。
解决此问题的简单方法是将temp
的副本追加到res
。
def subsets(nums):
res = []
backtrack(res, [], nums, 0)
return res
def backtrack(res, temp, nums, start):
res.append(temp[:])
for i in range(start, len(nums)):
temp.append(nums[i])
backtrack(res, temp, nums, i + 1)
temp.pop() # Backtrack
print(subsets([1, 2, 3]))
输出
[[], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3]]
FWIW,我意识到这个练习的要点是练习递归,但在Python中最好避免递归,除非您真的需要它(例如,用于处理像树这样的递归数据结构)。但这里有一个更紧凑的迭代解决方案。
def subsets(seq):
z = [[]]
for x in seq:
z += [y + [x] for y in z]
return z
若要了解其工作原理,我们可以对其进行一点扩展,然后添加print
调用。
def subsets(seq):
z = [[]]
for x in seq:
print('z =', z, 'x =', x)
w = []
for y in z:
w += [y + [x]]
z += w
return z
result = subsets([1, 2, 3])
print(result)
输出
z = [[]] x = 1
z = [[], [1]] x = 2
z = [[], [1], [2], [1, 2]] x = 3
[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
我们从包含单个空列表的列表z
开始。
在每个循环中,我们通过循环z
并使w
中的每个项都是z
中的相应项的副本来创建一个新的列表w
,并将当前的x
附加到该列表中。然后,我们使用w
的内容扩展z
。
只是为了好玩,这里有一个迭代生成器,它从位串(按自然顺序)生成子集。这种方法实际上非常有效,如果您希望在不消耗大量RAM的情况下获得一个大序列的所有子集,那么它是很好的。
def subsets(seq):
w = len(seq)
for i in range(1<<w):
yield [u for u, v in zip(seq, reversed('{:0{}b}'.format(i, w))) if v=='1']
print(*subsets([1, 2, 3]))
输出
[] [1] [2] [1, 2] [3] [1, 3] [2, 3] [1, 2, 3]
相关文章