在Java中获取一个集合的幂集

86

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

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

假设我有一个Java中的Set

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”。可能的解决方法是测试幂集中的每个成员。 - João Silva
7
这是谷歌面试中给软件开发人员的一个问题。这是一个刻意设计的问题,旨在测试您思维的敏捷性。 - Eric Leschinski
这是一个合理的问题。例如,要实现cribbage的评分函数,您必须测试幂集中的任何元素是否相加为15。 - John Henckel
28个回答

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
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

这个函数通过递归解决了这个问题,但是将变量命名为powerset作为全局变量:

static ArrayList<ArrayList<Integer>> powerSet = new ArrayList<>();

public static void getPowerSet(Queue<Integer> a) {
    int n = a.poll();
    if (!a.isEmpty()) {
        getPowerSet(a);
    }
    int s = powerSet.size();
    for (int i = 0; i < s; i++) {
        ArrayList<Integer> ne = new ArrayList<>();
        for (int j = 0; j < powerSet.get(i).size(); j++) {
            ne.add(powerSet.get(i).get(j));
        }
        ne.add(n);
        powerSet.add(ne);
    }
    ArrayList<Integer> p = new ArrayList<>();
    p.add(n);
    powerSet.add(p);
}

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
// 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;
    }

}

欢迎来到 Stack Overflow。您可能需要添加一些描述它是如何解决问题并帮助提问者的文本来完善这个答案。 - Lachlan Goodhew-Cook

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>();
}

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 class Combinations {

// A recursive method that prints all combinations of any size of the first n elements of an array
    public static void printCombinations(String[] arr, int n, String current) {
    // Base case: if n is 0, print the current combination
        if (n == 0) {
            System.out.println(current);
            return;
        }
    // Recursive case: for each element, either include it or exclude it from the current combination and recursively call printCombinations with n-1
        printCombinations(arr, n-1, current + arr[n-1] + " "); // include
        printCombinations(arr, n-1, current); // exclude
    }

    public static void main(String[] args) {
        // Read in the command line argument
       int n = Integer.parseInt(args[0]);
        // Well my array in this case consists of first 6 letters of the alphabet
        String[] arr = new String[n];
        arr[0] = "a";
        arr[1] = "b";
        arr[2] = "c";
        arr[3] = "d";
        arr[4] = "e";
        arr[5] = "f";
        // Call the recursive method with an empty current combination
        printCombinations(arr, n, "");
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接