【问题标题】:Printing all possible subsets of a list打印列表的所有可能子集
【发布时间】:2011-08-26 14:44:04
【问题描述】:

我有一个元素列表(1、2、3),我需要获取该列表的超集(幂集)(不重复元素)。所以基本上我需要创建一个列表,如下所示:

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

什么是最好的(在这种情况下简单> 效率,列表不会很大)实现这一点的方法是什么?最好使用 Java,但任何语言的解决方案都会很有用。

【问题讨论】:

  • 您想要该列表的所有子集。我建议递归。但是,如果您要处理的元素超过 30-40 个,您将无法处理拥有的 HUGE(超过 1TB 的数据)。这是干什么用的?
  • 您正在寻找的这种数据结构称为 Powerset(不同之处在于它还包含一个空集)。已经在 SO 上讨论过了。
  • 感谢 Zenzen 为我指明了正确的方向...我找到了 stackoverflow.com/questions/1670862/…。
  • 那些不是permutations,那些是子集。

标签: java algorithm powerset superset


【解决方案1】:

使用位掩码:

int allMasks = (1 << N);
for (int i = 1; i < allMasks; i++)
{
    for (int j = 0; j < N; j++)
        if ((i & (1 << j)) > 0) //The j-th element is used
           System.out.print((j + 1) + " ");

    System.out.println();
}

这里是所有位掩码:

1 = 001 = {1}
2 = 010 = {2}
3 = 011 = {1, 2}
4 = 100 = {3}
5 = 101 = {1, 3}
6 = 110 = {2, 3}
7 = 111 = {1, 2, 3}

你知道二进制的第一位是最右边的。

【讨论】:

  • 这很有趣......不过你显然比我聪明得多 - 给我一些时间来思考这个......N是原始元素的数量列表?我是否将列表中的对象映射到整数?
  • @Steve - 是的,N 是元素的数量 - 在上面的示例中为 N = 3。以二进制形式思考 - 如果不使用元素,则该位为 0,如果使用该元素,则该位为 1。例如 5 = 101 二进制。这意味着使用了1 和3。 = {1, 3}
  • 我认为这不适用于中等大小或以上的列表,allMasks 整数会溢出
  • @mancini0 - 它会在大约 31 位 (2^31) 后溢出,这是整数的最大值,它已经是 20 亿的巨大数字。如果你需要打印或迭代 20 亿个数字,那就有问题了 :)
  • @PetarMinchev 我同意 :),我唯一的用例是一个练习面试问题,我需要返回一个大小为 50 的列表的所有排列(2^50 个排列,10^15 个可能性)受谓词。为此,我修改了您的解决方案以使用大整数。修改后的版本会在 40 年左右产生正确的答案,但至少面试官不能说这是一个错误的解决方案。顺便说一句,你的解决方案很漂亮。
【解决方案2】:
import java.io.*;
import java.util.*;
class subsets
{
    static String list[];
    public static void process(int n)
    {
        int i,j,k;
        String s="";
        displaySubset(s);
        for(i=0;i<n;i++)
        {
            for(j=0;j<n-i;j++)
            {
                k=j+i;
                for(int m=j;m<=k;m++)
                {
                    s=s+m;
                }
                displaySubset(s);
                s="";
            }
        }
    }
    public static void displaySubset(String s)
    {
        String set="";
        for(int i=0;i<s.length();i++)
        {
            String m=""+s.charAt(i);
            int num=Integer.parseInt(m);
            if(i==s.length()-1)
                set=set+list[num];
            else
                set=set+list[num]+",";
        }
        set="{"+set+"}";
        System.out.println(set);
    }
    public static void main()
    {
        Scanner sc=new Scanner(System.in);
        System.out.println("Input ur list");
        String slist=sc.nextLine();
        int len=slist.length();
        slist=slist.substring(1,len-1);
        StringTokenizer st=new StringTokenizer(slist,",");
        int n=st.countTokens();
        list=new String[n];
        for(int i=0;i<n;i++)
        {
            list[i]=st.nextToken();
        }
        process(n);
    }
}

【讨论】:

  • 程序很简单。首先,我们尝试获取 1-n 位数字的所有可能组合,直到 1,2,3,....n 位数字的每个列表的最后一位为 n。然后用每个组合提取其每个字符(即数字)并显示存储在由该字符(数字)表示的单元格索引中的子集的元素。
  • 在答案本身中添加代码描述会比在 cmets 中更好。仅用代码回答通常不被社区认为是好的答案,即使代码正确回答了问题。
【解决方案3】:

一个基于Petar Minchev方案的java方案——

public static List<List<Integer>> getAllSubsets(List<Integer> input) {
    int allMasks = 1 << input.size();
    List<List<Integer>> output = new ArrayList<List<Integer>>();
    for(int i=0;i<allMasks;i++) {
        List<Integer> sub = new ArrayList<Integer>();
        for(int j=0;j<input.size();j++) {
            if((i & (1 << j)) > 0) {
                sub.add(input.get(j));
            }
        }
        output.add(sub);
    }

    return output;
}

【讨论】:

    【解决方案4】:

    我注意到答案集中在字符串列表上。 因此,我决定分享更通用的答案。 希望它会有所帮助。 (Soultion 是基于我找到的另一种解决方案,我将它结合到一个通用算法中。)

    /**
     * metod returns all the sublists of a given list
     * the method assumes all object are different
     * no matter the type of the list (generics)
     * @param list the list to return all the sublist of
     * @param <T>
     * @return list of the different sublists that can be made from the list object
     */
    public static <T>  List<List<T>>getAllSubLists(List<T>list)
    {
        List<T>subList;
        List<List<T>>res = new ArrayList<>();
        List<List<Integer>> indexes = allSubListIndexes(list.size());
        for(List<Integer> subListIndexes:indexes)
        {
            subList=new ArrayList<>();
            for(int index:subListIndexes)
                subList.add(list.get(index));
            res.add(subList);
        }
        return res;
    }
    /**
     * method returns list of list of integers representing the indexes of all the sublists in a N size list
     * @param n the size of the list
     * @return list of list of integers of indexes of the sublist
     */
    public static List<List<Integer>> allSubListIndexes(int n) {
        List<List<Integer>> res = new ArrayList<>();
        int allMasks = (1 << n);
        for (int i = 1; i < allMasks; i++)
        {
            res.add(new ArrayList<>());
            for (int j = 0; j < n; j++)
                if ((i & (1 << j)) > 0)
                    res.get(i-1).add(j);
    
        }
        return res;
    }
    

    【讨论】:

      【解决方案5】:

      这是一个简单的函数,可用于创建由给定数组或列表的所有可能子集的数字生成的所有可能数字的列表。

      void SubsetNumbers(int[] arr){
          int len=arr.length;
          List<Integer> list=new ArrayList<Integer>();
          List<Integer> list1=new ArrayList<Integer>();
          for(int n:arr){
              if(list.size()!=0){
                  for(int a:list){
                      list1.add(a*10+n);
                  }
                  list1.add(n);
                  list.addAll(list1);
                  list1.clear();
              }else{
                  list.add(n);
              }
          }
          System.out.println(list.toString());
      }
      

      【讨论】:

        【解决方案6】:

        在给定的解决方案中,我们遍历每个索引并包括当前和所有其他元素。

        class Solution {
                public List<List<Integer>> subsets(int[] nums) {
                    List<List<Integer>> ans = new ArrayList<>();
                    if(nums == null || nums.length ==0){
                        return ans;
                    }
                    Arrays.sort(nums);
                    List<Integer> subset = new ArrayList<>();
                    allSubset(nums, ans , subset , 0);
                    return ans;
                }
                private void allSubset(int[] nums,List<List<Integer>> ans ,List<Integer> subset , int idx){
                    ans.add(new ArrayList<>(subset));
                    for(int i = idx; i < nums.length; i++){
                        subset.add(nums[i]);
                        allSubset(nums, ans , subset , i+1);
                        subset.remove(subset.size() - 1);
                    }
                }
                
        }
        

        【讨论】:

          【解决方案7】:

          修改了 Peter Minchev 的解决方案以通过 BigInteger 处理更大的列表

          public static List<List<Integer>> getAllSubsets(List<Integer> input) {
              BigInteger allMasks = BigInteger.ONE.shiftLeft(input.size());
              List<List<Integer>> output = new ArrayList<>();
              for(BigInteger i=BigInteger.ZERO;allMasks.subtract(i).compareTo(BigInteger.ZERO)>0; i=i.add(BigInteger.ONE)) {
                  List<Integer> subList = new ArrayList<Integer>();
                  for(int j=0;j<input.size();j++) {
                      if(i.and(BigInteger.valueOf(1<<j)).compareTo(BigInteger.ZERO) > 0) {
                          subList.add(input.get(j));
                      }
                  }
                  System.out.println(subList);
                  output.add(subList);
              }
              return output;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-12-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多