Split a list into two sublists in all possible ways

那年仲夏 提交于 2019-11-28 21:32:16
l = [1, 2, 3, 4]
flags = [False] * len(l)
while True:
    a = [l[i] for i, flag in enumerate(flags) if flag]
    b = [l[i] for i, flag in enumerate(flags) if not flag]
    print a, b
    for i in xrange(len(l)):
        flags[i] = not flags[i]
        if flags[i]:
            break
    else:
        break

Result:

[] [1, 2, 3, 4]
[1] [2, 3, 4]
[2] [1, 3, 4]
[1, 2] [3, 4]
[3] [1, 2, 4]
[1, 3] [2, 4]
[2, 3] [1, 4]
[1, 2, 3] [4]
[4] [1, 2, 3]
[1, 4] [2, 3]
[2, 4] [1, 3]
[1, 2, 4] [3]
[3, 4] [1, 2]
[1, 3, 4] [2]
[2, 3, 4] [1]
[1, 2, 3, 4] []

It can easily be adapted to java:

public static void main(String[] args) {
    int[] l = new int[] { 1, 2, 3, 4 };
    boolean[] flags = new boolean[l.length];
    for (int i = 0; i != l.length;) {
        ArrayList<Integer> a = new ArrayList<>(), b = new ArrayList<>();
        for (int j = 0; j < l.length; j++)
            if (flags[j]) a.add(l[j]); else b.add(l[j]);
        System.out.println("" + a + ", " + b);
        for (i = 0; i < l.length && !(flags[i] = !flags[i]); i++);
    }
}

A more low-level solution using bitwise arithmetic to count subsets that should be easy to translate to Java:

def sublists(xs):
    l = len(xs)
    for i in range(1 << l):
        incl, excl = [], []
        for j in range(l):
            if i & (1 << j):
                incl.append(xs[j])
            else:
                excl.append(xs[j])
        yield (incl, excl)

Though in Python, it's quite easy to get the result with its extensive library, in Java, you can write a recursive solution. The following will print all possible combinations of your array:

public static void main(String[] args) {
    List<Integer> num = Arrays.asList(1, 2, 3, 4);
    List<List<Integer>> sublists = new ArrayList<List<Integer>>();
    for (int i = 0; i <= num.size(); i++) {
      permutation(num, sublists, i, new ArrayList<Integer>(), 0);
    }

    for (List<Integer> subList : sublists) {
        List<Integer> numCopy = new ArrayList<Integer>(num);
        numCopy.removeAll(subList);
        System.out.println("(" + subList + ", " + numCopy + ")");
    }
}

public static void permutation(List<Integer> nums, List<List<Integer>> subLists, int sublistSize, List<Integer> currentSubList,
      int startIndex) {
    if (sublistSize == 0) {
      subLists.add(currentSubList);
    } else {
      sublistSize--;
      for (int i = startIndex; i < nums.size(); i++) {
        List<Integer> newSubList = new ArrayList<Integer>(currentSubList);
        newSubList.add(nums.get(i));
        permutation(nums, subLists, sublistSize, newSubList, i + 1);
      }
    }
}

The sublists carries all the combinations found till now. The last parameter is the startIndex for the next element of current sublist. That is to avoid duplicates.

Going over all the different sizes of combinations and "subtracting" them from the original list seems intuitive approach IMO:

from itertools import combinations

s = [1, 2, 3, 4]
for combs in (combinations(s, r) for r in range(len(s)+1))  :
    for comb in combs:
        diff = list(set(s[:]) - set(comb))
        print diff, list(comb)

OUTPUT

[1, 2, 3, 4] []
[2, 3, 4] [1]
[1, 3, 4] [2]
[1, 2, 4] [3]
[1, 2, 3] [4]
[3, 4] [1, 2]
[2, 4] [1, 3]
[2, 3] [1, 4]
[1, 4] [2, 3]
[1, 3] [2, 4]
[1, 2] [3, 4]
[4] [1, 2, 3]
[3] [1, 2, 4]
[2] [1, 3, 4]
[1] [2, 3, 4]
[] [1, 2, 3, 4]

The same approach can be applied with Java (only that it's more verbose...):

private static List<Integer> initial;

public static void main(String[] args) throws IOException {
    initial = Arrays.asList(1, 2, 3);
    combinations(initial);
}

static void combinations(List<Integer> src) {
    combinations(new LinkedList<>(), src);
}

private static void combinations(LinkedList<Integer> prefix, List<Integer> src) {        
    if (src.size() > 0) {
        prefix = new LinkedList<>(prefix); //create a copy to not modify the orig
        src = new LinkedList<>(src); //copy
        Integer curr = src.remove(0);
        print(prefix, curr); // <-- this is the only thing that shouldn't appear in a "normal" combinations method, and which makes it print the list-pairs
        combinations(prefix, src); // recurse without curr
        prefix.add(curr);
        combinations(prefix, src); // recurse with curr
    }
}

// print the prefix+curr, as one list, and initial-(prefix+curr) as a second list
private static void print(LinkedList<Integer> prefix, Integer curr) {
    prefix = new LinkedList<>(prefix); //copy
    prefix.add(curr);
    System.out.println(Arrays.toString(prefix.toArray()) +
                    " " + Arrays.toString(subtract(initial, prefix).toArray()));
}

private static List<Integer> subtract(List<Integer> initial, LinkedList<Integer> prefix) {
    initial = new LinkedList<>(initial); //copy
    initial.removeAll(prefix);
    return initial;
}

OUTPUT

[1] [2, 3]
[2] [1, 3]
[3] [1, 2]
[2, 3] [1]
[1, 2] [3]
[1, 3] [2]
[1, 2, 3] []
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!