在Java中获取集合的幂集


86

的幂集{1, 2, 3}是:

{{}, {2}, {3}, {2, 3}, {1, 2}, {1, 3}, {1, 2, 3}, {1}}

假设我有一个SetJava语言:

Set<Integer> mySet = new HashSet<Integer>();
mySet.add(1);
mySet.add(2);
mySet.add(3);
Set<Set<Integer>> powerSet = getPowerset(mySet);

如何编写具有最佳可能复杂度的函数getPowerset?(我认为可能是O(2 ^ n)。)


7
假设您有一组配置(例如“ A”,“ B”和“ C”),可用于参数化模型,并且您想查看哪个子集产生最佳结果,例如“ A” ”。可能的解决方案是测试电源集的每个成员。
若昂·席尔瓦

7
这是针对软件开发人员的Google面试问题。测试您的思维敏捷性是一个人为的问题。
埃里克·莱斯钦斯基

这是一个合理的问题。例如,为了实现对纸牌的计分函数,你必须测试幂是否有元素加起来是15
约翰·亨克尔

Answers:


101

是的,O(2^n)确实如此,因为您需要生成2^n可能的组合。这是一个使用泛型和集合的有效实现:

public static <T> Set<Set<T>> powerSet(Set<T> originalSet) {
    Set<Set<T>> sets = new HashSet<Set<T>>();
    if (originalSet.isEmpty()) {
        sets.add(new HashSet<T>());
        return sets;
    }
    List<T> list = new ArrayList<T>(originalSet);
    T head = list.get(0);
    Set<T> rest = new HashSet<T>(list.subList(1, list.size())); 
    for (Set<T> set : powerSet(rest)) {
        Set<T> newSet = new HashSet<T>();
        newSet.add(head);
        newSet.addAll(set);
        sets.add(newSet);
        sets.add(set);
    }       
    return sets;
}  

根据您的示例输入进行测试:

 Set<Integer> mySet = new HashSet<Integer>();
 mySet.add(1);
 mySet.add(2);
 mySet.add(3);
 for (Set<Integer> s : SetUtils.powerSet(mySet)) {
     System.out.println(s);
 }

1
使用Iterator而不是使用list会更快吗?例如:Set <T> rest = new HashSet <T>(originalSet); Iterator <T> i = rest.iterator(); T头= i.next(); i.remove(); ?
迪玛斯

1
@CosminVacaroiu ...它还能做什么?
user253751

3
您确定是O(2^n)吗?这就是功率组中的组数,但是每个组都必须在内存中创建,这至少需要花费与组大小成比例的时间。根据wolfram alpha,它位于O(n * 2^n)wolfram alpha查询
fabian

1
即使集合的大小在10 ^ 5的数量级,这也行得通吗?
bane19

1
@GauravShankar 2 ^ 100 = 2 ^(10 ^ 2)已经大于10 ^ 30。无论您要在哪台图灵机上进行计算,都不会目睹计算完成。
Karl Richter)

31

实际上,我已经编写了满足您在O(1)中要求的代码。问题是您打算如何使用Set下一步。如果要调用size()它,则为O(1),但是如果要对其进行迭代,则显然为O(2^n)

contains()将会是O(n),等等。

您真的需要这个吗?

编辑:

该代码现在已在Guava中提供,并通过方法公开Sets.powerSet(set)


我需要遍历每个子集
Manuel Araoz 09年

但是您是否需要存储每个子集?
finnw


如果我只想要具有完全k个元素的幂集怎么办?您的代码对此有效吗?
Eyal

新链接(番石榴移至Github)
yiwei

12

这是我使用发电机的一种解决方案,其优势在于,永远不会一次存储整个功率集...因此,您可以一次又一次地对其进行迭代,而无需将其存储在内存中。我想认为这是一个更好的选择...请注意,复杂度是相同的,O(2 ^ n),但是内存需求减少了(假定垃圾收集器的行为!!))

/**
 *
 */
package org.mechaevil.util.Algorithms;

import java.util.BitSet;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

/**
 * @author st0le
 *
 */
public class PowerSet<E> implements Iterator<Set<E>>,Iterable<Set<E>>{
    private E[] arr = null;
    private BitSet bset = null;

    @SuppressWarnings("unchecked")
    public PowerSet(Set<E> set)
    {
        arr = (E[])set.toArray();
        bset = new BitSet(arr.length + 1);
    }

    @Override
    public boolean hasNext() {
        return !bset.get(arr.length);
    }

    @Override
    public Set<E> next() {
        Set<E> returnSet = new TreeSet<E>();
        for(int i = 0; i < arr.length; i++)
        {
            if(bset.get(i))
                returnSet.add(arr[i]);
        }
        //increment bset
        for(int i = 0; i < bset.size(); i++)
        {
            if(!bset.get(i))
            {
                bset.set(i);
                break;
            }else
                bset.clear(i);
        }

        return returnSet;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not Supported!");
    }

    @Override
    public Iterator<Set<E>> iterator() {
        return this;
    }

}

要调用它,请使用以下模式:

        Set<Character> set = new TreeSet<Character> ();
        for(int i = 0; i < 5; i++)
            set.add((char) (i + 'A'));

        PowerSet<Character> pset = new PowerSet<Character>(set);
        for(Set<Character> s:pset)
        {
            System.out.println(s);
        }

来自我的欧拉计划图书馆... :)


番石榴的工作原理与此非常相似,但仅限于32个元素。这不是不合理的,因为2 ** 32可能有太多迭代。它使用的内存比您少,因为它仅在需要时才生成AbstractSet。尝试对Guava编写代码,在其中仅打印10,000个元素中的1个,并举一个大例子。我敢打赌,番石榴会更快。
Eyal 2014年

@Eyal,我敢肯定,但我从未声明过。我自己写的,它不是用于生产代码的。这是算法的练习。
st0le 2014年

1
次要说明:您的“ returnSet”是一个TreeSet,它要求其各项具有可比性。事实并非如此。考虑将其交换为HashSet或LinkedHashSet
Joris Kinable

10

如果n <63,这是一个合理的假设,因为您将耗尽内存(除非使用迭代器实现),无论如何尝试构造幂集,这是一种更简洁的方法。二进制操作比Math.pow()掩码数组和数组操作快得多,但是Java用户不知何故担心它们……

List<T> list = new ArrayList<T>(originalSet);
int n = list.size();

Set<Set<T>> powerSet = new HashSet<Set<T>>();

for( long i = 0; i < (1 << n); i++) {
    Set<T> element = new HashSet<T>();
    for( int j = 0; j < n; j++ )
        if( (i >> j) % 2 == 1 ) element.add(list.get(j));
    powerSet.add(element); 
}

return powerSet;

for循环中的终止条件应为i <(2 << n-1),而不是i <(1 << n-1)。
bazeusz 2014年

感谢@bazeusz,我将其更改i < (1 << n)为等效名称。
Andrew Mao

9

是一个精确描述所需内容(包括代码)的教程。您是正确的,因为复杂度为O(2 ^ n)。


2
是不是复杂度(n * 2 ^ n)?由于二进制字符串的长度为n,因此在主循环的每次迭代中,我们都会迭代整个二进制字符串。
玛姬

1
教程很棒,但是我使用这种技术解决了HackerRank问题:它仅通过了一半的测试用例,而另一半则由于超时或导致运行时错误而失败。
Eugenia Ozirna '17

7

我根据@Harry He的想法提出了另一个解决方案。可能不是最优雅的,但据我了解,它就在这里:

让我们以经典的简单示例为例,SP(S)的PowerSet = {{1},{2},{3}}。我们知道获得子集数量的公式为2 ^ n(7 +空集)。对于此示例,2 ^ 3 = 8个子集。

为了找到每个子集,我们需要将0-7小数转换为二进制表示,如下转换表所示:

转换表

如果我们逐行遍历表,则每一行将产生一个子集,并且每个子集的值将来自启用的位。

Bin值部分中的每一列对应于原始输入集中的索引位置。

这是我的代码:

public class PowerSet {

/**
 * @param args
 */
public static void main(String[] args) {
    PowerSet ps = new PowerSet();
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    set.add(3);
    for (Set<Integer> s : ps.powerSet(set)) {
        System.out.println(s);
    }
}

public Set<Set<Integer>> powerSet(Set<Integer> originalSet) {
    // Original set size e.g. 3
    int size = originalSet.size();
    // Number of subsets 2^n, e.g 2^3 = 8
    int numberOfSubSets = (int) Math.pow(2, size);
    Set<Set<Integer>> sets = new HashSet<Set<Integer>>();
    ArrayList<Integer> originalList = new ArrayList<Integer>(originalSet);
    for (int i = 0; i < numberOfSubSets; i++) {
        // Get binary representation of this index e.g. 010 = 2 for n = 3
        String bin = getPaddedBinString(i, size);
        //Get sub-set
        Set<Integer> set = getSet(bin, originalList));
        sets.add(set);
    }
    return sets;
}

//Gets a sub-set based on the binary representation. E.g. for 010 where n = 3 it will bring a new Set with value 2
private Set<Integer> getSet(String bin, List<Integer> origValues){
    Set<Integer> result = new HashSet<Integer>();
    for(int i = bin.length()-1; i >= 0; i--){
        //Only get sub-sets where bool flag is on
        if(bin.charAt(i) == '1'){
            int val = origValues.get(i);
            result.add(val);
        }
    }
    return result;
}

//Converts an int to Bin and adds left padding to zero's based on size
private String getPaddedBinString(int i, int size) {
    String bin = Integer.toBinaryString(i);
    bin = String.format("%0" + size + "d", Integer.parseInt(bin));
    return bin;
}

}

5

如果使用的是Eclipse Collections(以前称为GS Collections),则可以powerSet()在所有SetIterables上使用该方法。

MutableSet<Integer> set = UnifiedSet.newSetWith(1, 2, 3);
System.out.println("powerSet = " + set.powerSet());
// prints: powerSet = [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]

注意:我是Eclipse Collections的提交者。


您可以共享和解释您的解决方案的代码吗?
康拉德·霍夫纳2014年

3
你可以看一下通过代码在这里:github.com/goldmansachs/gs-collections/blob/...
克雷格P. Motlin

4

我一直在寻找一个不如此处发布的解决方案那么庞大的解决方案。它以Java 7为目标,因此对于版本5和6将需要少量粘贴。

Set<Set<Object>> powerSetofNodes(Set<Object> orig) {
    Set<Set<Object>> powerSet = new HashSet<>(),
        runSet = new HashSet<>(),
        thisSet = new HashSet<>();

    while (powerSet.size() < (Math.pow(2, orig.size())-1)) {
        if (powerSet.isEmpty()) {
            for (Object o : orig) {
                Set<Object> s = new TreeSet<>();
                s.add(o);
                runSet.add(s);
                powerSet.add(s);
            }
            continue;
        }
        for (Object o : orig) {
            for (Set<Object> s : runSet) {
                Set<Object> s2 = new TreeSet<>();
                s2.addAll(s);
                s2.add(o);
                powerSet.add(s2);
                thisSet.add(s2);
            }
        }
        runSet.clear();
        runSet.addAll(thisSet);
        thisSet.clear();
    }
    powerSet.add(new TreeSet());
    return powerSet;

这是一些示例代码进行测试:

Set<Object> hs = new HashSet<>();
hs.add(1);
hs.add(2);
hs.add(3);
hs.add(4);
for(Set<Object> s : powerSetofNodes(hs)) {
    System.out.println(Arrays.toString(s.toArray()));
}

powerSetofNodes()末尾是否不缺少“}”吗?
彼得·莫滕森

3

当集合的大小很大时,上述某些解决方案会受到影响,因为它们会创建大量要收集的对象垃圾并需要复制数据。我们如何避免这种情况?我们可以利用以下事实:我们知道结果集的大小将是(2 ^ n),预分配一个大的数组,然后追加到它的末尾,再也不会复制。

加速随着n迅速增长。我将其与上述JoãoSilva的解决方案进行了比较。在我的机器上(所有测量值近似),n = 13快5倍,n = 14是7x,n = 15是12x,n = 16是25x,n = 17是75x,n = 18是140x。因此,垃圾创建/收集和复制在其他类似的big-O解决方案中占主导地位。

与让它动态增长相比,在开始时预分配数组似乎是一个胜利。在n = 18的情况下,动态增长大约需要两倍的时间。

public static <T> List<List<T>> powerSet(List<T> originalSet) {
    // result size will be 2^n, where n=size(originalset)
    // good to initialize the array size to avoid dynamic growing
    int resultSize = (int) Math.pow(2, originalSet.size());
    // resultPowerSet is what we will return
    List<List<T>> resultPowerSet = new ArrayList<List<T>>(resultSize);

    // Initialize result with the empty set, which powersets contain by definition
    resultPowerSet.add(new ArrayList<T>(0)); 

    // for every item in the original list
    for (T itemFromOriginalSet : originalSet) {

        // iterate through the existing powerset result
        // loop through subset and append to the resultPowerset as we go
        // must remember size at the beginning, before we append new elements
        int startingResultSize = resultPowerSet.size();
        for (int i=0; i<startingResultSize; i++) {
            // start with an existing element of the powerset
            List<T> oldSubset = resultPowerSet.get(i);

            // create a new element by adding a new item from the original list
            List<T> newSubset = new ArrayList<T>(oldSubset);
            newSubset.add(itemFromOriginalSet);

            // add this element to the result powerset (past startingResultSize)
            resultPowerSet.add(newSubset);
        }
    }
    return resultPowerSet;
}

3

以下解决方案是从我的《编码访谈:问题,分析与解决方案》一书中借用的:

在数组中选择了一些组成组合的整数。利用一组位,其中每个位代表数组中的整数。如果选择第i个字符进行组合,则第i个位为1;否则,第i个位为1。否则为0。例如,三个位用于数组[1、2、3]的组合。如果选择前两个整数1和2组成组合[1,2],则相应的位为{1,1、0}。类似地,对应于另一组合[1,3]的位是{1,0,1}。如果我们可以获得n位的所有可能组合,那么我们就能获得长度为n的数组的所有组合。

数字由一组位组成。n位的所有可能组合对应于1到2 ^ n -1的数字。因此,介于1和2 ^ n -1之间的每个数字都对应于长度为n的数组的组合。例如,数字6由位{1,1,0}组成,因此在数组[1、2、3]中选择第一个和第二个字符以生成组合[1、2]。类似地,具有位{1、0、1}的数字5对应于组合[1、3]。

实现此解决方案的Java代码如下所示:

public static ArrayList<ArrayList<Integer>> powerSet(int[] numbers) {
    ArrayList<ArrayList<Integer>> combinations = new ArrayList<ArrayList<Integer>>(); 
    BitSet bits = new BitSet(numbers.length);
    do{
        combinations.add(getCombination(numbers, bits));
    }while(increment(bits, numbers.length));

    return combinations;
}

private static boolean increment(BitSet bits, int length) {
    int index = length - 1;

    while(index >= 0 && bits.get(index)) {
        bits.clear(index);
        --index;
    }

    if(index < 0)
        return false;

    bits.set(index);
    return true;
}

private static ArrayList<Integer> getCombination(int[] numbers, BitSet bits){
    ArrayList<Integer> combination = new ArrayList<Integer>();
    for(int i = 0; i < numbers.length; ++i) {
        if(bits.get(i))
            combination.add(numbers[i]);
    }

    return combination;
}

方法增量增加了一组位中表示的数量。该算法从最右边的位清除1位,直到找到0位为止。然后,它将最右边的0位设置为1。例如,为了将位数{1,0,1}增加到5,它从右侧清除1位并将最右边的0位设置为1。 {1,1,0}代表数字6,这是将5加1的结果。


我修改了两件事:循环访问getCombination而不是number.length(或bits.size()),可以迭代到bits.length(),从而略微加快了生成速度。最后,针对我的问题,我按大小对子集进行了排序。
BoLe

3

这是一个简单的迭代O(2 ^ n)解决方案:

public static Set<Set<Integer>> powerSet(List<Integer> intList){

    Set<Set<Integer>> result = new HashSet();
    result.add(new HashSet());

    for (Integer i : intList){

        Set<Set<Integer>> temp = new HashSet();

        for(Set<Integer> intSet : result){

            intSet = new HashSet(intSet);
            intSet.add(i);                
            temp.add(intSet);
        }
        result.addAll(temp);
    }
    return result;
}

此解决方案还使用O(2 ^ n)空间,这对于大型输入集而言太大了。最好使用堆栈或队列来代替递归,以遵循递归定义。
rossb83

2
import java.util.Set;
import com.google.common.collect.*;

Set<Set<Integer>> sets = Sets.powerSet(ImmutableSet.of(1, 2, 3));

1

如果S是具有N个元素的有限集,则S的幂集包含2 ^ N个元素。简单枚举幂集元素的时间为2 ^ N,因此O(2^N)(急切地)构建幂集的时间复杂度的下限也为2 ^ N。

简而言之,任何涉及创建功率集的计算都不会针对N的较大值进行扩展。除了避免创建功率集之外,没有任何聪明的算法可以帮助您...


1

一种不递归的方法如下:使用二进制掩码并进行所有可能的组合。

public HashSet<HashSet> createPowerSet(Object[] array)
{
    HashSet<HashSet> powerSet=new HashSet();
    boolean[] mask= new boolean[array.length];

    for(int i=0;i<Math.pow(2, array.length);i++)
    {
        HashSet set=new HashSet();
        for(int j=0;j<mask.length;j++)
        {
            if(mask[i])
                set.add(array[j]);
        }
        powerSet.add(set);      

        increaseMask(mask);
    }

    return powerSet;
}

public void increaseMask(boolean[] mask)
{
    boolean carry=false;

    if(mask[0])
        {
            mask[0]=false;
            carry=true;
        }
    else
        mask[0]=true;

    for(int i=1;i<mask.length;i++)
    {
        if(mask[i]==true && carry==true)
        mask[i]=false;
        else if (mask[i]==false && carry==true)
        {
            mask[i]=true;
            carry=false;
        }
        else 
            break;

    }

}

1

算法:

输入:Set [],set_size1。获取功率集的大小powet_set_size = pow(2,set_size)2计数器从0到pow_set_size的循环(a)i = 0到set_size(i)的循环如果计数器中的第i位为设置此子集的集合中的第ith个打印元素(b)子集(即换行符)的打印分隔符

#include <stdio.h>
#include <math.h>
 
void printPowerSet(char *set, int set_size)
{
    /*set_size of power set of a set with set_size
      n is (2**n -1)*/
    unsigned int pow_set_size = pow(2, set_size);
    int counter, j;
 
    /*Run from counter 000..0 to 111..1*/
    for(counter = 0; counter < pow_set_size; counter++)
    {
      for(j = 0; j < set_size; j++)
       {
          /* Check if jth bit in the counter is set
             If set then pront jth element from set */
          if(counter & (1<<j))
            printf("%c", set[j]);
       }
       printf("\n");
    }
}
 
/*Driver program to test printPowerSet*/
int main()
{
    char set[] = {'a','b','c'};
    printPowerSet(set, 3);
 
    getchar();
    return 0;
}


1

这是我的递归解决方案,可以使用Java泛型获得任何集合的幂集。它的主要思想是将输入数组的开头与其余数组的所有可能解决方案组合如下。

import java.util.LinkedHashSet;
import java.util.Set;

public class SetUtil {
    private static<T>  Set<Set<T>> combine(T head, Set<Set<T>> set) {
        Set<Set<T>> all = new LinkedHashSet<>();

        for (Set<T> currentSet : set) {
            Set<T> outputSet = new LinkedHashSet<>();

            outputSet.add(head);
            outputSet.addAll(currentSet);

            all.add(outputSet);
        }

        all.addAll(set);        

        return all;
    }

    //Assuming that T[] is an array with no repeated elements ...
    public static<T> Set<Set<T>> powerSet(T[] input) {
        if (input.length == 0) {
            Set <Set<T>>emptySet = new LinkedHashSet<>();

            emptySet.add(new LinkedHashSet<T>());

            return emptySet;
        }

        T head = input[0];
        T[] newInputSet = (T[]) new Object[input.length - 1];

        for (int i = 1; i < input.length; ++i) {
            newInputSet[i - 1] = input[i];
        }

        Set<Set<T>> all = combine(head, powerSet(newInputSet));

        return all;
    }

    public static void main(String[] args) {            
        Set<Set<Integer>> set = SetUtil.powerSet(new Integer[] {1, 2, 3, 4, 5, 6});

        System.out.println(set);
    }
}

这将输出:

[[1, 2, 3, 4, 5, 6], [1, 2, 3, 4, 5], [1, 2, 3, 4, 6], [1, 2, 3, 4], [1, 2, 3, 5, 6], [1, 2, 3, 5], [1, 2, 3, 6], [1, 2, 3], [1, 2, 4, 5, 6], [1, 2, 4, 5], [1, 2, 4, 6], [1, 2, 4], [1, 2, 5, 6], [1, 2, 5], [1, 2, 6], [1, 2], [1, 3, 4, 5, 6], [1, 3, 4, 5], [1, 3, 4, 6], [1, 3, 4], [1, 3, 5, 6], [1, 3, 5], [1, 3, 6], [1, 3], [1, 4, 5, 6], [1, 4, 5], [1, 4, 6], [1, 4], [1, 5, 6], [1, 5], [1, 6], [1], [2, 3, 4, 5, 6], [2, 3, 4, 5], [2, 3, 4, 6], [2, 3, 4], [2, 3, 5, 6], [2, 3, 5], [2, 3, 6], [2, 3], [2, 4, 5, 6], [2, 4, 5], [2, 4, 6], [2, 4], [2, 5, 6], [2, 5], [2, 6], [2], [3, 4, 5, 6], [3, 4, 5], [3, 4, 6], [3, 4], [3, 5, 6], [3, 5], [3, 6], [3], [4, 5, 6], [4, 5], [4, 6], [4], [5, 6], [5], [6], []]

1

另一个示例实现:

 public static void main(String args[])
    {
        int[] arr = new int[]{1,2,3,4};
        // Assuming that number of sets are in integer range
        int totalSets = (int)Math.pow(2,arr.length);
        for(int i=0;i<totalSets;i++)
        {
            String binaryRep = Integer.toBinaryString(i);      
            for(int j=0;j<binaryRep.length();j++)
            {
                int index=binaryRep.length()-1-j;
                if(binaryRep.charAt(index)=='1')
                System.out.print(arr[j] +" ");       
            }
            System.out.println();
        }
    }

1

这是我使用lambda的方法。

public static <T> Set<Set<T>> powerSet(T[] set) {
      return IntStream
            .range(0, (int) Math.pow(2, set.length))
            .parallel() //performance improvement
            .mapToObj(e -> IntStream.range(0, set.length).filter(i -> (e & (0b1 << i)) != 0).mapToObj(i -> set[i]).collect(Collectors.toSet()))
            .map(Function.identity())
            .collect(Collectors.toSet());
        }

或并行(请参见parallel()注释):

输入集大小:18

逻辑处理器:8à3.4GHz

性能提升:30%


1

t的子集是可以通过删除t的零个或多个元素而形成的任何集。无首子集将添加缺少第一个元素的t的子集,而for循环将处理添加有第一个元素的子集。例如,如果t包含元素[“ 1”,“ 2”,“ 3”],missingFirst将添加[[“”],[“ 2”],[“ 3”],[“ 2”,“ 3 “]]和for循环会将“ 1”粘贴在这些元素的前面,并将其添加到newSet中。因此,我们将以[[“”],[“ 1”],[“ 2”],[“ 3”],[“ 1”,“ 2”],[“ 1”,“ 3”]结尾,[“ 2”,“ 3”],[“ 1”,“ 2”,“ 3”]]。

public static Set<Set<String>> allSubsets(Set<String> t) {
        Set<Set<String>> powerSet = new TreeSet<>();
        if(t.isEmpty()) {
            powerSet.add(new TreeSet<>());
            return powerSet;
        }
        String first = t.get(0);
        Set<Set<String>> withoutFirst = allSubsets(t.subSet(1, t.size()));
        for (List<String> 1st : withoutFirst) {
            Set<String> newSet = new TreeSet<>();
            newSet.add(first);
            newSet.addAll(lst);
            powerSet.add(newSet);
        }
        powerSet.addAll(withoutFirst);
        return powerSet;
    }

请考虑在您提供的代码中添加简短说明。
Mirza Sisic

它甚至没有编译,似乎是用一些幻想的Java版本编写的。 Set没有get带索引的subSet方法,也没有方法;1st不是有效的标识符(我想lst是故意的)。将所有集合更改为列表,它几乎可以编译...
john16384 '19

0
// input: S
// output: P
// S = [1,2]
// P = [], [1], [2], [1,2]

public static void main(String[] args) {
    String input = args[0];
    String[] S = input.split(",");
    String[] P = getPowerSet(S);
    if (P.length == Math.pow(2, S.length)) {
        for (String s : P) {
            System.out.print("[" + s + "],");
        }
    } else {
        System.out.println("Results are incorrect");
    }
}

private static String[] getPowerSet(String[] s) {
    if (s.length == 1) {
        return new String[] { "", s[0] };
    } else {
        String[] subP1 = getPowerSet(Arrays.copyOfRange(s, 1, s.length));
        String[] subP2 = new String[subP1.length];
        for (int i = 0; i < subP1.length; i++) {
            subP2[i] = s[0] + subP1[i];
        }
        String[] P = new String[subP1.length + subP2.length];
        System.arraycopy(subP1, 0, P, 0, subP1.length);
        System.arraycopy(subP2, 0, P, subP1.length, subP2.length);
        return P;
    }

}

欢迎使用堆栈溢出。您可能希望通过一些描述该问题正在做什么以及如何解决提问者问题的文本来充实这个答案。
Lachlan Goodhew-Cook

0

最近,我不得不使用类似的方法,但是首先需要最小的子列表(包含1个元素,然后是2个元素,...)。我不想包括空列表或整个列表。另外,我不需要返回的所有子列表的列表,我只需要对每个子列表做一些事情。

想要做到这一点而无需递归,并提出了以下建议(将“正在做的事情”抽象为功能接口):

@FunctionalInterface interface ListHandler<T> {
    void handle(List<T> list);
}


public static <T> void forAllSubLists(final List<T> list, ListHandler handler) {
    int     ll = list.size();   // Length of original list
    int     ci[] = new int[ll]; // Array for list indices
    List<T> sub = new ArrayList<>(ll);  // The sublist
    List<T> uml = Collections.unmodifiableList(sub);    // For passing to handler

    for (int gl = 1, gm; gl <= ll; gl++) {  // Subgroup length 1 .. n-1
        gm = 0; ci[0] = -1; sub.add(null);  // Some inits, and ensure sublist is at least gl items long

        do {
                ci[gm]++;                       // Get the next item for this member

                if (ci[gm] > ll - gl + gm) {    // Exhausted all possibilities for this position
                        gm--; continue;         // Continue with the next value for the previous member
                }

                sub.set(gm, list.get(ci[gm]));  // Set the corresponding member in the sublist

                if (gm == gl - 1) {             // Ok, a sublist with length gl
                        handler.handle(uml);    // Handle it
                } else {
                        ci[gm + 1] = ci[gm];    // Starting value for next member is this 
                        gm++;                   // Continue with the next member
                }
        } while (gm >= 0);  // Finished cycling through all possibilities
    }   // Next subgroup length
}

这样,将其限制为特定长度的子列表也很容易。


0
public class PowerSet {
    public static List<HashSet<Integer>> powerset(int[] a) {
        LinkedList<HashSet<Integer>> sets = new LinkedList<HashSet<Integer>>();
        int n = a.length;
        for (int i = 0; i < 1 << n; i++) {
            HashSet<Integer> set = new HashSet<Integer>();
            for (int j = 0; j < n; j++) {
                if ((1 << j & i) > 0)
                    set.add(a[j]);
            }
            sets.add(set);
        }
        return sets;
    }

    public static void main(String[] args) {
        List<HashSet<Integer>> sets = PowerSet.powerset(new int[]{ 1, 2, 3 });
        for (HashSet<Integer> set : sets) {
            for (int i : set)
                System.out.print(i);
            System.out.println();
        } 
    }
}

0

另一个解决方案-使用Java8 +流式api,它是惰性的和有序的,因此当与“ limit()”一起使用时,它将返回正确的子集。

 public long bitRangeMin(int size, int bitCount){
    BitSet bs = new BitSet(size);
    bs.set(0, bitCount);
    return bs.toLongArray()[0];
}

public long bitRangeMax(int size, int bitCount){
    BitSet bs = BitSet.valueOf(new long[]{0});
    bs.set(size - bitCount, size);
    return bs.toLongArray()[0];
}

public <T> Stream<List<T>> powerSet(Collection<T> data)
{
    List<T> list = new LinkedHashSet<>(data).stream().collect(Collectors.toList());
    Stream<BitSet> head = LongStream.of(0).mapToObj( i -> BitSet.valueOf(new long[]{i}));
    Stream<BitSet> tail = IntStream.rangeClosed(1, list.size())
            .boxed()
            .flatMap( v1 -> LongStream.rangeClosed( bitRangeMin(list.size(), v1), bitRangeMax(list.size(), v1))
                    .mapToObj(v2 -> BitSet.valueOf(new long[]{v2}))
                    .filter( bs -> bs.cardinality() == v1));

    return Stream.concat(head, tail)
            .map( bs -> bs
                    .stream()
                    .mapToObj(list::get)
                    .collect(Collectors.toList()));
}

客户端代码是

@Test
public void testPowerSetOfGivenCollection(){
    List<Character> data = new LinkedList<>();
    for(char i = 'a'; i < 'a'+5; i++ ){
        data.add(i);
    }
    powerSet(data)
            .limit(9)
            .forEach(System.out::print);

}

/ *打印:[] [a] [b] [c] [d] [e] [a,b] [a,c] [b,c] * /


0

我们可以使用或不使用递归来编写幂集。这是没有递归的尝试:

public List<List<Integer>> getPowerSet(List<Integer> set) {
    List<List<Integer>> powerSet = new ArrayList<List<Integer>>();
    int max = 1 << set.size();
    for(int i=0; i < max; i++) {
        List<Integer> subSet = getSubSet(i, set);
        powerSet.add(subSet);
    }
    return powerSet;
}

private List<Integer> getSubSet(int p, List<Integer> set) {
    List<Integer> subSet = new ArrayList<Integer>();
    int position = 0;
    for(int i=p; i > 0; i >>= 1) {
        if((i & 1) == 1) {
            subSet.add(set.get(position));
        }
        position++;
    }
    return subSet;
}

0

这是生成功率集。这个想法是第一个= S[0],较小的集合是S[1,...n]

计算smallerSet的所有子集,并将它们放入所有子集中。

对于所有子集中的每个子集,将其克隆并首先添加到子集中。

ArrayList<ArrayList<Integer>> getSubsets(ArrayList<Integer> set, int index){
    ArrayList<ArrayList<Integer>> allsubsets;
    if(set.size() == index){
        allsubsets = new ArrayList<ArrayList<Integer>>();
        allsubsets.add(new ArrayList<Integer>()); // the empty set 
    }else{
        allsubsets = getSubsets(set, index+1);
        int item = set.get(index);

        ArrayList<ArrayList<Integer>> moresubsets = new ArrayList<ArrayList<Integer>>();

        for(ArrayList<Integer> subset: allsubsets){
            ArrayList<Integer> newsubset = new ArrayList<Integer>();

            newsubset.addAll(subset);
            newsubset.add(item);
            moresubsets.add(newsubset);

        }

        moresubsets.addAll(moresubsets);

    }

    return allsubsets;
}

0
package problems;

import java.util.ArrayList;
import java.util.List;

public class SubsetFinderRecursive {
    public static void main(String[] args) {
        //input
        int[] input = new int[3];
        for(int i=0; i<input.length; i++) {
            input[i] = i+1;
        }
        // root node of the tree
        Node root = new Node();

        // insert values into tree
        for(int i=0; i<input.length; i++) {
            insertIntoTree(root, input[i]);
        }

        // print leaf nodes for subsets
        printLeafNodes(root);
    }

    static void printLeafNodes(Node root) {

        if(root == null) {
            return;
        }

        // Its a leaf node
        if(root.left == null && root.right == null) {
            System.out.println(root.values);
            return;
        }

        // if we are not at a leaf node, then explore left and right

        if(root.left !=null) {
            printLeafNodes(root.left);
        }

        if(root.right != null) {
            printLeafNodes(root.right);
        }
    }

    static void insertIntoTree(Node root, int value) {

        // Error handling
        if(root == null) {
            return;
        }

        // if there is a sub tree then go down
        if(root.left !=null && root.right != null) {
            insertIntoTree(root.left, value);
            insertIntoTree(root.right, value);
        }

        // if we are at the leaf node, then we have 2 choices
        // Either exclude or include
        if(root.left == null && root.right == null) {
            // exclude
            root.left = new Node();
            root.left.values.addAll(root.values);
            // include
            root.right = new Node();
            root.right.values.addAll(root.values);
            root.right.values.add(value);
            return;
        }
    }

}

class Node {
    Node left;
    Node right;
    List<Integer> values = new ArrayList<Integer>();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.