Search code examples
javaalgorithmsublist

Split a list into n sublists in all possible ways


I want to split a list into a given number n sublists in all possible ways in Java.

For example [1, 2, 3, 4] where n = 3 would include the following lists (but would not be a complete solution - complete would require much more space):

([], [], [1,2,3,4])
([],[1],[2,3,4])
([],[1,2],[3,4])
([],[1,2,3],[4])
([],[1,2,3,4],[])
([1],[2,3,4], [])
([1],[2,3],[4])
([2,3],[4],[1])
([4],[],[1,2,3])
...

etc

I adapted a solution from another similar question (Split a list into two sublists in all possible ways) however it only works for creating lists of 2 sublists and I am struggling to grasp how to implement it for a flexible rather than hardcoded number of sublists.

Here is my code:

public List<List<EGroup>> permutation(List<E> list) {
        List<List<E>> sublists = new ArrayList<List<E>>();
        for (int i = 0; i <= list.size(); i++) {
          permutationSplit(list, sublists, i, new ArrayList<E>(), 0);
        }

        List<List<EGroup>> listOfEGroupPairs = new ArrayList<List<EGroup>>();
       
        for (List<E> subList : sublists) {
            List<E> listCopy = new ArrayList<E>(list);
            listCopy.removeAll(subList);
            EGroup e1 = new EGroup(subList);
            EGroup e2 = new EGroup(listCopy);   
            List<EGroup> egr = new ArrayList<EGroup>();
            egr.add(e1);
            egr.add(e2);
            listOfEGroupPairs.add(egr);
        }
        
        return listOfEGroupPairs;
    }

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

I need to create n number of EGroup objects to add to listOfEGroupPairs rather than the hardcoded 2, but how to always get the right number (n) of sublists of varied size each loop?


Solution

  • If I understand the question correctly, each element of the original list can end up in any of the n sublists. That means, there are n^s possible sublists (s being the number of elements in the original list), which can be enumerated in a simple loop. With a bit of modulo and integer division you can then get the proper "bucket" for each element and prepare the results accordingly.

    public <T> List<List<List<T>>> partition(List<T> lst, int n) {
        var result = new ArrayList<List<List<T>>>();
        // k = SUM ( pos of lst[i] * n^i ) 
        for (int k = 0; k < Math.pow(n, lst.size()); k++) {
            // initialize result
            List<List<T>> res = IntStream.range(0, n)
                    .mapToObj(i -> new ArrayList<T>())
                    .collect(Collectors.toList());
            // distribute elements to sub-lists
            int k2 = k;
            for (int i = 0; i < lst.size(); i++) {
                res.get(k2 % n).add(lst.get(i));
                k2 /= n;
            }
            result.add(res);
        }
        return result;
    }