我正在处理一些采访问题,其中一个使用回溯的经典问题是计算列表的powerset(所有子集)。我有解决问题的java解决方案:
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, 0);
return list;
}
private void backtrack(List<List<Integer>> list , List<Integer> {
tempList, int [] nums, int start){
list.add(new ArrayList<>(tempList));
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backtrack(list, tempList, nums, i + 1);
tempList.remove(tempList.size() - 1);
}
}
我正在尝试做同样的事情,但改为使用python:
class Solution:
def subsets(self, nums):
lista = []
self.backtrack(lista, [], sorted(nums), 0)
return lista
def backtrack(self, lista, tempList, nums, start):
lista.append(tempList)
for i in range(start, len(nums)):
tempList.append(nums[i])
self.backtrack(lista, tempList, nums, i+1)
tempList.pop()
我打印了tempList,它似乎按预期工作。我认为这个问题是它的lista.append(tempList)没有像我预期的那样工作。 python中有一些特殊性会产生这种不同的结果吗?谢谢!
答案 0 :(得分:3)
list.add(new ArrayList<>(tempList));
制作tempList
的副本,你也应该在Python版本中做到这一点!
lista.append(tempList[:])
# OR
lista.append(list(tempList)) # which is closer in syntax to your java version
答案 1 :(得分:1)
您需要制作tempList
的副本。否则,您只需将对同一tempList
个对象的多个引用附加到lista
。当backtrack
最终返回时,tempList
为空,因此lista
将只包含对该空列表的多个引用。
这是编写代码的更多Pythoic方式。此版本不需要制作副本,因为每次递归调用都会创建一个新的templist
,而不是修改原始templist
对象。
def subsets(nums):
lista = []
backtrack(lista, [], sorted(nums), 0)
return lista
def backtrack(lista, templist, nums, start):
lista.append(templist)
for i in range(start, len(nums)):
backtrack(lista, templist + nums[i:i+1], nums, i+1)
# Test
for t in subsets([1,2,3]):
print(t)
<强>输出强>
[]
[1]
[1, 2]
[1, 2, 3]
[1, 3]
[2]
[2, 3]
[3]
只是为了好玩,这里以生成器函数的形式使用相同的算法。我已经取消了排序步骤,如果他们真的需要,调用者可以对输入进行排序。
def subsets(nums, templist=None, start=0):
if templist is None:
templist = []
yield templist
for i in range(start, len(nums)):
yield from subsets(nums, templist + nums[i:i+1], i+1)
for t in subsets([1, 2, 3]):
print(t)
此代码生成与早期代码相同的输出。
这是一种非递归方式,可以使子集更加紧凑。它使用与早期代码类似的算法,但它以稍微不同的顺序生成子集。在纸上手工完成算法可能会有所帮助,看看它是如何工作的。
def subsets(seq):
z = [[]]
for x in seq:
z += [y + [x] for y in z]
return z
for t in subsets([1, 2, 3]):
print(t)
<强>输出强>
[]
[1]
[2]
[1, 2]
[3]
[1, 3]
[2, 3]
[1, 2, 3]
这是该算法几乎不可读的单行版本。 ;)
from functools import reduce
def subsets(seq):
return reduce(lambda z, x: z + [y + [x] for y in z], seq, [[]])
最后,这是一种使用二进制数制作子集的方法。如果您希望获得特定的子集而不生成所有子集,这将非常方便。
给定n个项目的集合S,有2**n
个子集。 S的每个子集对应一个n位二进制数:数字中的1位表示S中的对应项位于子集中。
def subsets(seq):
for i in range(1<<len(seq)):
a = []
for u in seq:
if not i:
break
if i & 1:
a.append(u)
i >>= 1
yield a
此生成器生成的子集与最后两个版本的顺序相同。
答案 2 :(得分:0)
itertools module的标准文档中已经解决了这个问题(powerset迭代器):
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
解决最后一段的问题:
tempList.append(n)将参数作为一个整体附加(因为列表在Python中是异构的,能够包含所有类型)。你可以用 temPist.extend(n)代替它,它可以像你期望的那样移动元素。