使用java方法计算python中列表的所有子集

时间:2017-11-14 07:43:34

标签: java python algorithm backtracking

我正在处理一些采访问题,其中一个使用回溯的经典问题是计算列表的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中有一些特殊性会产生这种不同的结果吗?谢谢!

3 个答案:

答案 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)代替它,它可以像你期望的那样移动元素。