Java 查找列表的所有可能拆分的算法

Java 查找列表的所有可能拆分的算法,java,algorithm,Java,Algorithm,我正在使用我创建的一个链表,其中包含一组数字作为数据。我需要找到一种方法来测试这个列表中每一个可能的两集分区,为了做到这一点,我需要将列表分解成每一个可能的两集组合。顺序不重要,会有重复的 For instance, for a list of numbers {1 4 3 1}, the possible splits are {1} and {4, 3, 1} {4} and {1, 3, 1} {3} and {1, 4, 1} {1} and {1, 4, 3} {1, 4} and

我正在使用我创建的一个链表,其中包含一组数字作为数据。我需要找到一种方法来测试这个列表中每一个可能的两集分区,为了做到这一点,我需要将列表分解成每一个可能的两集组合。顺序不重要,会有重复的

For instance, for a list of numbers {1 4 3 1}, the possible splits are 

{1} and {4, 3, 1}
{4} and {1, 3, 1}
{3} and {1, 4, 1}
{1} and {1, 4, 3}
{1, 4} and {3, 1}
{1, 3} and {4, 1}
{1, 1} and {4, 3}
4个数字的列表并不难,但随着列表越来越大,事情变得越来越复杂,我很难看到一个模式。有人能帮我找到一个算法吗

编辑:

对不起,我没有看到问题。这就是我迄今为止所尝试的。我的循环结构是错误的。在尝试使用常规数组之后,当我弄清楚我在做什么时,我将扩展该算法以适合我的链表

public class TwoSubsets
{
    public static void main(String[] args)
    {
        int[] list = {1, 3, 5, 7, 8};
        int places = 1;

        int[] subsetA = new int[10];
        int[] subsetB = new int[10];


        for (int i = 0; i < list.length; i++)
        {
            subsetA[i] = list[i];       
            for (int current = 0; current < (5 - i ); current++)
            {
                subsetB[current] = list[places];        
                places++;

            }

            System.out.print("subsetA = ");
            for (int j = 0; j < subsetA.length; j++)
            {
                System.out.print(subsetA[j] + " ");
            }

            System.out.println();
            System.out.print("subsetB = ");
            for (int k = 0; k < subsetB.length; k++)
            {
                System.out.print(subsetB[k] + " ");
            }



        }
    }

}
公共类二子集
{
公共静态void main(字符串[]args)
{
int[]list={1,3,5,7,8};
整数位数=1;
int[]子集=新的int[10];
int[]subsetB=新的int[10];
for(int i=0;i
因此,除了互补子集之外,您正在寻找给定集合的所有(适当)子集。如果列表中有n个元素,则将有2^n个子集。但是,由于您不需要空的子集,并且希望用(B,A)标识分区(A,B),因此得到2^(n-1)-1个分区

要枚举它们,您可以用一个包含n个数字的二进制数标识一个分区,其中位置k中的数字0表示列表的第k个元素位于分区的第一个集合中,1表示它位于第二个集合中。您想用一个互补的数字来标识一个数字(用另一个交换一个集合),并想排除0(空子集)

因此,您可以使用位操作。XOR运算符提供互补细分。因此,类似于以下内容的内容应该会起作用:

int m = (1<<n)-1; // where n is the number of elements, m=111111...11 in binary
for (int i=0;i<m-1;++i) {
    if (i>(m^i)) continue; // this was already considered with 0 and 1 exchanged
    // here the binary digits of i represent the partition
    for (int j=0;j<n;++j) {
       if ((1<<j) & i) {
          // the j-th element of the list goes into the second set of the partition
       } else {
          // the j-th element of the list goes into the first set of the partition
       }
    }
}

int m=(1使用链表存储子集实际上是非常理想的——代码比使用数组更容易组合在一起

编写一个构建子集的递归函数。它将采用以下参数:

  • “输入”列表
  • “输出”列表
  • 结果向量(这将是一个“收集参数”,如果这对您有任何意义的话)
以下是Ruby中的粗略代码草图:

 # 'input', and 'output' are linked-list nodes
 # we'll assume they have 'value' and 'next' attributes
 # we'll further assume that a new node can be allocated with Node.new(value,next)
 # the lists are null-terminated

 def build_subsets(input, output, results)
   if input.nil?
     results << output
   else
     item = input.value
     input = input.next
     build_subsets(input, Node.new(item, output), results)
     build_subsets(input, output, results)
   end
 end
在tha之后,所有子集都将显示在
结果中。我知道您需要Java翻译,但这应该很容易翻译成Java。我只是告诉您代码的工作原理。

代码:

public static void main(String[] args) {
    for(String element : findSplits(list)) {
        System.out.println(element);
    }        
}

static ArrayList<String> findSplits(ArrayList<Integer> set) {
    ArrayList<String> output = new ArrayList();
    ArrayList<Integer> first = new ArrayList(), second = new ArrayList();
    String bitString;
    int bits = (int) Math.pow(2, set.size());
    while (bits-- > 0) {
        bitString = String.format("%" + set.size() + "s", Integer.toBinaryString(bits)).replace(' ', '0');
        for (int i = 0; i < set.size(); i++) {
            if (bitString.substring(i, i+1).equals("0")) {
                first.add(set.get(i));
            } else {
                second.add(set.get(i));
            }
        }
        if (first.size() < set.size() && second.size() < set.size()) {
            if (!output.contains(first + " " + second) && !output.contains(second + " " + first)) {
                output.add(first + " " + second);
            }
        }
        first.clear();
        second.clear();
    }
    return output;
}

这是否符合您的要求?如果没有,请告诉我,我将根据需要进行调整或添加评论。

我添加了一个编辑,以显示我迄今为止所做的工作。但是,我的问题似乎是“暂停”只需要添加重复删除部分。删除什么?if…continue按子集的顺序删除重复项。在{1,1,2}的情况下,您应该生成分区{[1,2],[1]}和{[1,1],[2]}。但是您的解决方案将给出{[1,2],[1]}两次,因为输入中有两个1。一个来自二进制数100,另一个来自010。
public static void main(String[] args) {
    for(String element : findSplits(list)) {
        System.out.println(element);
    }        
}

static ArrayList<String> findSplits(ArrayList<Integer> set) {
    ArrayList<String> output = new ArrayList();
    ArrayList<Integer> first = new ArrayList(), second = new ArrayList();
    String bitString;
    int bits = (int) Math.pow(2, set.size());
    while (bits-- > 0) {
        bitString = String.format("%" + set.size() + "s", Integer.toBinaryString(bits)).replace(' ', '0');
        for (int i = 0; i < set.size(); i++) {
            if (bitString.substring(i, i+1).equals("0")) {
                first.add(set.get(i));
            } else {
                second.add(set.get(i));
            }
        }
        if (first.size() < set.size() && second.size() < set.size()) {
            if (!output.contains(first + " " + second) && !output.contains(second + " " + first)) {
                output.add(first + " " + second);
            }
        }
        first.clear();
        second.clear();
    }
    return output;
}
[1] [1, 4, 3]
[3] [1, 4, 1]
[3, 1] [1, 4]
[4] [1, 3, 1]
[4, 1] [1, 3]
[4, 3] [1, 1]
[4, 3, 1] [1]