42

我想找到一组整数的子集。这是带有回溯的“子集和”算法的第一步。我已经编写了以下代码,但它没有返回正确的答案:

BTSum(0, nums);
///**************
ArrayList<Integer> list = new ArrayList<Integer>();

public static ArrayList<Integer> BTSum(int n, ArrayList<Integer> numbers) {
    if (n == numbers.size()) {
        for (Integer integer : list) {
            System.out.print(integer+", ");
        }
        System.out.println("********************");
        list.removeAll(list);
        System.out.println();
    } else {
        for (int i = n; i < numbers.size(); i++) {
            if (i == numbers.size() - 1) {
                list.add(numbers.get(i));
                BTSum(i + 1, numbers);
            } else {
                list.add(numbers.get(i));
                for (int j = i+1; j < numbers.size(); j++)
                BTSum(j, numbers);
            }
        }
    }

    return null;
}

例如,如果我想计算 set = {1, 3, 5} 的子集,我的方法的结果是:

 1, 3, 5, ********************

 5, ********************

 3, 5, ********************

 5, ********************

 3, 5, ********************

 5, ********************

我希望它产生:

1, 3, 5 
1, 5
3, 5
5

我认为问题出在部分 list.removeAll(list); 但我不知道如何纠正它。

4

16 回答 16

88

你想要的是一个Powerset。这是它的一个简单实现:

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

我将举一个例子来解释该算法如何适用于 的幂集{1, 2, 3}

  • 删除,并为; {1}执行 powerset{2, 3}
    • 删除,并为; {2}执行 powerset{3}
      • 删除,并为; {3}执行 powerset{}
        • 的幂集{}{{}};
      • Powerset of{3}与=3结合使用;{{}}{ {}, {3} }
    • Powerset of{2, 3}与={2}结合使用;{ {}, {3} }{ {}, {3}, {2}, {2, 3} }
  • 的幂集{1, 2, 3}与={1}结合。{ {}, {3}, {2}, {2, 3} }{ {}, {3}, {2}, {2, 3}, {1}, {3, 1}, {2, 1}, {2, 3, 1} }
于 2011-01-09T16:09:48.247 回答
24

只是一个入门如何解决问题

方法一

  • 获取号码列表的第一个元素
  • 从剩余的数字列表中生成所有子集(即没有选择的数字列表)=> 递归!
  • 对于在上一步中找到的每个子集,将子集本身和与在步骤 1 中选择的元素连接的子集添加到输出中。

当然,您必须检查基本情况,即您的号码列表是否为空。

方法二

n一个有元素的集合有子集是一个众所周知的事实2^n。因此,您可以计算二进制 from 0to2^n并将二进制数解释为相应的子集。请注意,这种方法需要一个具有足够位数的二进制数来表示整个集合。

将两种方法中的一种转换为代码应该不是太大的问题。

于 2011-01-09T15:57:12.107 回答
15

你的代码真的很混乱,没有解释。

您可以使用确定集合中的数字的位掩码进行迭代。例如,从 0 到 2^n 的每个数字在其二进制表示中都有一个唯一的子集

对于 n = 3:

i = 5 -> 101 二进制,选择第一个和最后一个元素 i = 7 -> 111 二进制,选择前 3 个元素

假设有 n 个元素(n < 64,毕竟如果 n 大于 64,你将永远运行它)。

for(long i = 0; i < (1<<n); i++){
    ArrayList<Integer> subset = new ArrayList<Integer>();
    for(int j = 0; j < n; j++){
        if((i>>j) & 1) == 1){ // bit j is on
            subset.add(numbers.get(j));
        }
    }
    // print subset
}
于 2011-01-09T15:59:28.563 回答
10

考虑一个菜鸟访客(感谢谷歌)这个问题 -像我一样
这是一个适用于简单主体的递归解决方案:

Set = {a,b,c,d,e}
然后我们可以将其分解为{a}+Subset of {b,c,d,e}

public class Powerset{
     String str = "abcd"; //our string
     public static void main(String []args){
        Powerset ps = new Powerset();
        for(int i = 0; i< ps.str.length();i++){ //traverse through all characters
            ps.subs("",i);
        }
     }

     void subs(String substr,int index)
     {
         String s = ""+str.charAt(index); //very important, create a variable on each stack
         s = substr+s; //append the subset so far
         System.out.println(s); //print

         for(int i=index+1;i<str.length();i++)
           subs(s,i); //call recursively

     }
}

输出

a
ab
abc
abcd
abd
ac
acd
ad
b
bc
bcd
bd
c
cd
d
于 2014-07-11T10:03:39.690 回答
4

很明显,任何给定集合的子集总数等于 2^(集合中的元素数)。如果设置

A = {1, 2, 3}

那么 A 的子集是:

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

如果我们看它就像二进制数。

{000}、{001}、{010}、{011}、{100}、{101}、{110}、{111}

如果我们考虑以上:

static void subSet(char[] set) {
        int c = set.length;

        for (int i = 0; i < (1 << c); i++) {
            System.out.print("{");
            for (int j = 0; j < c; j++) {
                if ((i & (1 << j)) > 0) {
                    System.out.print(set[j] + " ");
                }
            }
            System.out.println("}");
        }
    }

    public static void main(String[] args) {
        char c[] = {'a', 'b', 'c'};
        subSet(c);
    }
于 2016-06-15T12:11:30.210 回答
2
private static void findSubsets(int array[])
{
  int numOfSubsets = 1 << array.length; 

  for(int i = 0; i < numOfSubsets; i++)
 {
    int pos = array.length - 1;
   int bitmask = i;

   System.out.print("{");
   while(bitmask > 0)
   {
    if((bitmask & 1) == 1)
     System.out.print(array[pos]+",");
    bitmask >>= 1;
    pos--;
   }
   System.out.print("}");
 }
}
于 2012-12-17T22:22:44.900 回答
2

根据我今天学到的,这里是Java解决方案它基于recursion

public class Powerset {

    public static void main(String[] args) {
        final List<List<String>> allSubsets = powerSet(Arrays.asList(1, 2, 3, 4), 0);
        for (List<String> subsets : allSubsets) {
            System.out.println(subsets);
        }
    }

    private static List<List<String>> powerSet(final List<Integer> values,
                                               int index) {
        if (index == values.size()) {
            return new ArrayList<>();
        }
        int val = values.get(index);
        List<List<String>> subset = powerSet(values, index + 1);
        List<List<String>> returnList = new ArrayList<>();
        returnList.add(Arrays.asList(String.valueOf(val)));
        returnList.addAll(subset);
        for (final List<String> subsetValues : subset) {
            for (final String subsetValue : subsetValues) {
                returnList.add(Arrays.asList(val + "," + subsetValue));
            }
        }
        return returnList;
    }
}

运行它会给出结果

[1]
[2]
[3]
[4]
[3,4]
[2,3]
[2,4]
[2,3,4]
[1,2]
[1,3]
[1,4]
[1,3,4]
[1,2,3]
[1,2,4]
[1,2,3,4]
于 2014-01-19T22:46:10.967 回答
1

我实际上是在尝试解决这个问题,并在上​​一篇文章中得到了算法@phimuemue。这是我实现的。希望这有效。

/**
*@Sherin Syriac
*
*/

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

public class SubSet {
    ArrayList<List<Integer>> allSubset = new ArrayList<List<Integer>>();

    /**
     * @param args
     */
    public static void main(String[] args) {
        SubSet subSet = new SubSet();
        ArrayList<Integer> set = new ArrayList<Integer>();
        set.add(1);
        set.add(2);
        set.add(3);
        set.add(4);
        subSet.getSubSet(set, 0);
        for (List<Integer> list : subSet.allSubset) {
            System.out.print("{");
            for (Integer element : list) {
                System.out.print(element);
            }
            System.out.println("}");
        }

    }

    public void getSubSet(ArrayList<Integer> set, int index) {
        if (set.size() == index) {
            ArrayList<Integer> temp = new ArrayList<Integer>();
            allSubset.add(temp);
        } else {
            getSubSet(set, index + 1);
            ArrayList<List<Integer>> tempAllSubsets = new ArrayList<List<Integer>>();
            for (List subset : allSubset) {
                ArrayList<Integer> newList = new ArrayList<Integer>();
                newList.addAll(subset);
                newList.add(set.get(index));
                tempAllSubsets.add(newList);
            }

            allSubset.addAll(tempAllSubsets);
        }

    }

}
于 2011-02-24T21:42:26.580 回答
1
// subsets for the set of 5,9,8

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

public class Subset {
    public static void main(String[] args) {
    List<Integer> s = new ArrayList<Integer>();
    s.add(9);
    s.add(5);
    s.add(8);
    int setSize = s.size();
    int finalValue = (int) (Math.pow(2, setSize));
    String bValue = "";
    for (int i = 0; i < finalValue; i++) {
        bValue = Integer.toBinaryString(i);
        int bValueSize = bValue.length();
        for (int k = 0; k < (setSize - bValueSize); k++) {
            bValue = "0" + bValue;
        }
        System.out.print("{ ");
        for (int j = 0; j < setSize; j++) {
            if (bValue.charAt(j) == '1') {
                System.out.print((s.get(j)) + " ");
            }
        }
        System.out.print("} ");
    }
}
}


//Output : { } { 8 } { 5 } { 5 8 } { 9 } { 9 8 } { 9 5 } { 9 5 8 } 
于 2012-03-19T08:19:45.337 回答
1
public static ArrayList<ArrayList<Integer>> powerSet(List<Integer> intList) {

    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
    result.add(new ArrayList<Integer>());

    for (int i : intList) {
        ArrayList<ArrayList<Integer>> temp = new ArrayList<ArrayList<Integer>>();

        for (ArrayList<Integer> innerList : result) {
            innerList = new ArrayList<Integer>(innerList);
            innerList.add(i);
            temp.add(innerList);
        }
        result.addAll(temp);
    }

    return result;
}
于 2016-01-27T07:58:52.660 回答
1

如果您正在处理大量元素,您可能(尽管不太可能)遇到堆栈溢出问题。我承认你更有可能在溢出堆栈之前耗尽内存,但无论如何我都会把这个非递归方法放在这里。

public static final <T> Set<Set<T>> powerSet(final Iterable<T> original) {
  Set<Set<T>> sets = new HashSet<>();
  sets.add(new HashSet<>());

  for (final T value : original) {
    final Set<Set<T>> newSets = new HashSet<>(sets);

    for (final Set<T> set : sets) {
      final Set<T> newSet = new HashSet<>(set);
      newSet.add(value);
      newSets.add(newSet);
    }

    sets = newSets;
  }

  return sets;
}

或者,如果您更愿意处理数组:

@SuppressWarnings("unchecked")
public static final <T> T[][] powerSet(final T... original) {
  T[][] sets = (T[][]) Array.newInstance(original.getClass(), 1);
  sets[0] = Arrays.copyOf(original, 0);

  for (final T value : original) {
    final int oldLength = sets.length;
    sets = Arrays.copyOf(sets, oldLength * 2);

    for (int i = 0; i < oldLength; i++) {
      final T[] oldArray = sets[i];
      final T[] newArray = Arrays.copyOf(oldArray, oldArray.length + 1);
      newArray[oldArray.length] = value;
      sets[i + oldLength] = newArray;
    }
  }

  return sets;
}
于 2018-08-30T19:50:06.180 回答
1

使用递归获取所有子集(在类似的行中解决书中的排列:用Java递归思考)

public class ChapterSix {

public static void main(String[] args) {
    new ChapterSix().listSubSets("", "123");
}

void listSubSets(String prefix, String s) {
    System.out.println(prefix);

    if("".equals(s)) {
        return;
    } else {
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            String rest = s.substring(i + 1);
            listSubSets(prefix + ch, rest);
        }
    }
}
}

输出:

1
12
123
13
2
23
3
于 2019-10-27T22:21:07.367 回答
1

简单的 Java 递归解决方案 -

private static List<List<Integer>> allsubSet(List<Integer> integers, int start, int end) {

       //Base case if there is only one element so there would be two subset 
       // empty list and that element
       if(start == end) {
            List<List<Integer>> result = new ArrayList<>();

            List<Integer> emptyList = new ArrayList<>();
            result.add(emptyList);

            List<Integer> element = new ArrayList<>();
            element.add(integers.get(start));
            result.add(element );

            return result;
        }
        //I know if by recursion we can expect that we'll get the n-1 correct result

       List<List<Integer>> lists = allsubSet(integers, start, end-1);

    //here i copy all the n-1 results and just added the nth element in expected results

        List<List<Integer>> copyList =  new ArrayList<>(lists);
        for (List<Integer> list : lists) {
            List<Integer> copy=  new ArrayList<>(list);
            copy.add(integers.get(end));
            copyList.add(copy);
        }
        return copyList;
    }


为了避免冗余,我们可以简单地使用 Set 代替 List

于 2019-09-09T16:21:13.873 回答
1
public static void printSubsets(int[] arr) {
    for (int start = 0; start < arr.length; start++) { // iterate through each element of the array
        for (int i = 0; i < arr.length - start; i++) { // find number of subsets for the element
            int[] tmp = new int[i + 1]; // calculate a temporal array size 
            for (int j = 0; j < tmp.length; j++) { // populate the array with corresponding elements
                tmp[j] = arr[start + j];
            }
            System.out.println(Arrays.toString(tmp));
        }
    }
}
于 2021-07-28T19:21:26.250 回答
0

这是一些伪代码。您可以通过在执行过程中存储每个调用的值以及在递归调用检查调用值是否已经存在之前来减少相同的递归调用。

以下算法将具有除空集之外的所有子集。

list * subsets(string s, list * v){
    if(s.length() == 1){
        list.add(s);    
        return v;
    }
    else
    {
        list * temp = subsets(s[1 to length-1], v);     
        int length = temp->size();

        for(int i=0;i<length;i++){
            temp.add(s[0]+temp[i]);
        }

        list.add(s[0]);
        return temp;
    }
}
于 2013-02-16T01:49:44.550 回答
0

这是打印给定数字集的所有子集的逻辑。这也称为集合的幂集。我使用 Java 使用了一种简单的递归方法来解决这个问题,但您也可以相应地用其他语言编写代码。

import java.util.Scanner;

public class PowerSubset {

    public static void main(String[] args) {

        // HardCoded Input
         int arr[] = { 1, 2, 3 };//original array whose subset is to be found
         int n=3; //size of array

        // Dynamic Input
        /*Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int arr[] = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }*/

        int data[] = new int[arr.length]; // temporary array

        printSubset(arr, data, n, 0, 0);
    }

    public static void printSubset(int arr[], int data[], int n, int dataIndex, int arrIndex) {
        if (arrIndex == n) { //comparing with n since now you are at the leaf node
            System.out.print("[");//watch pictorial chart in the below video link 
            for (int j = 0; j < n; j++) {
                System.out.print(data[j] == 0 ? "" : data[j]);
            }
            System.out.print("]");
            System.out.println();
            return;
        }
        data[dataIndex] = arr[arrIndex];
        printSubset(arr, data, n, dataIndex + 1, arrIndex + 1);//recursive call 1
        data[dataIndex] = 0;
        printSubset(arr, data, n, dataIndex, arrIndex + 1);//recursive call 2
    }

}

上述代码的输出:

[123]
[12]
[13]
[1]
[23]
[2]
[3]
[]
于 2019-05-06T18:12:50.793 回答