【问题标题】:Calculating all of the subsets of a set of numbers计算一组数字的所有子集
【发布时间】:2011-06-06 02:49:41
【问题描述】:

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

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(列表); 但我不知道如何纠正它。

【问题讨论】:

标签: java algorithm subset


【解决方案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));
        }
    }
}

【讨论】:

  • 我想知道这个解决方案的时间复杂度
【解决方案2】:

这是打印给定数字集的所有子集的逻辑。这也称为集合的幂集。我使用了一种简单的递归方法来使用 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]
[]

【讨论】:

    【解决方案3】:

    使用递归获取所有子集(在用于解决书中排列的类似行:用 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
    

    【讨论】:

      【解决方案4】:

      简单的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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          很明显,任何给定集合的子集总数等于 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);
              }
          

          【讨论】:

            【解决方案7】:

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

            设置 = {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
            

            【讨论】:

            • 实现+1的简单优雅的解决方案
            • 喜欢这个解决方案 - 毫无疑问,这是要走的路,谢谢你,如果我理解正确地思考为什么这个递归有效的方式是:我们从 a,b,c 开始,d 作为最小的子集然后当我们想要查看每个更大的子集时,我们说 a->ab,ac,ad 然后 b->bc,bd(不是 ab,因为我们已经有了它)等等,这就是为什么推进索引是正确的方法。再次感谢
            • 是的......我在“ab”存储在递归堆栈中的逻辑下对其进行编码,然后使用它并将“c”附加到它......所以“abc”在新的输出递归堆栈。打印它并继续追加! :)
            【解决方案8】:
            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;
            }
            

            【讨论】:

              【解决方案9】:

              您想要的称为 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},并对{2, 3}执行powerset;
                • 删除{2},并对{3}执行powerset;
                  • 删除{3},并对{}执行powerset;
                    • {} 的幂集是{{}}
                  • {3} 的 Powerset 是 3 结合 {{}} = { {}, {3} };
                • {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} } 的组合。

              【讨论】:

              • 这是 2^n 时间复杂度吗?
              • @user3335040,是的,因为你总是需要生成 2^n 个集合。
              • 我无法理解 {2, 3} 的 step Powerset 的逻辑是 {2} 结合 { {}, {3} } = { {}, {3}, {2}, { 2, 3} };因为在上一步中 head 有 3 而 rest 是空的。
              • @Harish 逻辑是上一步加上上一步中的所有内容+新元素。所以你有{{}, {3}},它保留在新的集合中,并且你将每个添加2并将它们组合在一起。所以你会有{} + 2 = {2}{3} + 2 = {2,3}。结合前面的你有{{} , {3}, {2}, {2,3}}
              • 这是 O(n^2) 的解决方案。是否有可能获得时间复杂度更高的解决方案?
              【解决方案10】:

              根据我今天学到的,这里是 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]
              

              【讨论】:

                【解决方案11】:

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

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

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

                【讨论】:

                  【解决方案12】:
                  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("}");
                   }
                  }
                  

                  【讨论】:

                    【解决方案13】:
                    // 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 } 
                    

                    【讨论】:

                      【解决方案14】:

                      我实际上是在尝试解决这个问题,并在上​​一篇文章中得到了算法@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);
                              }
                      
                          }
                      
                      }
                      

                      【讨论】:

                        【解决方案15】:

                        您的代码确实令人困惑,没有任何解释。

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

                        对于 n = 3:

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

                        假设有 n 个元素(n

                        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
                        }
                        

                        【讨论】:

                        • 您能否详细说明 f((i>>j) & 1) == 1)。我知道您正在向右移动 i 位,将 j 零添加到 i 的二进制表示中。我也理解位明智的 & 将 i >>j 与 1 进行比较,并查看 i >> j 和 1 之间的所有位是否都打开。但我只是不明白为什么这个操作是添加子集的时间
                        • @BrianOgden (i&gt;&gt;j)&amp;1==1 只是测试i 中的第 j 位是否已设置。数字中的第 j 位对应于子集是否包含第 j 个对象。我正在循环 0 到 2n-1.
                        • 感谢@MichaelAnderson,这有点帮助,但为什么第 j 位对应于包含第 j 个对象的子集?
                        • 不是包含 j-th 对象的 子集 - 因为可能不止一个。如果设置了i 中的j-th 位,则i-th 子集包含j-th 对象。我对您的单独问题(stackoverflow.com/questions/33337317/…)提供了更完整的解释
                        【解决方案16】:

                        只是您如何解决问题的入门:

                        方法一

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

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

                        方法2

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

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

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 2018-11-02
                          • 1970-01-01
                          • 2020-08-25
                          • 2017-10-22
                          • 1970-01-01
                          相关资源
                          最近更新 更多