给定一个整形数组,求其子集(Java)

一、矢量构造法

具体实现我个人解释不清楚,看代码吧

public class Subset {
    public List<List<Integer>> Vector_construction(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        res.add(new ArrayList<>());//先把空集添加进去
        int i, j, n = nums.length;
        for (i = 0; i < n; ++i) {//将第i个数加入前置所有集合中,这边一定不能写i<nums.lengh
        //for (j = res.size() - 1; j >= 0; --j) {
//j-1是res list中最后一个集合的位置,这边可以改成先用一个temp存储res的大小,然后再从0开始遍历
            int size = res.size();
            for (j = 0; j < size; j++) {
                List<Integer> tmp = new ArrayList<>(res.get(j));
                tmp.add(nums[i]);
                res.add(tmp);
            }
        }
        return res;//返回结果
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4};
        Subset subset = new Subset();
        //test 1
        System.out.println(subset.Vector_construction(nums));
    }
}

 二、位向量法

实际上就是暴力搜索DFS
利用boolean数组表示第i个选还是不选
多加几个参数会减小代码的可读性,但是可以提升代码的运行效率,减少使用全局变量

这里DFS回溯还有一种写法是利用for循环来递归,这里用了一个参数代替了for循环,具体不过多赘述

public class Subset {
    public void Bit_vector(int[] nums, int n, int index, Boolean[] is_checked, List<List<Integer>> res) {
        if (index == n) {
            List<Integer> tmp = new ArrayList<>();
            for (int i = 0; i < n; ++i)
                if (is_checked[i]) tmp.add(nums[i]);

            res.add(tmp);//存储结果
            return;//递归结束条件
        }
        //选
        is_checked[index] = true;
        Bit_vector(nums, n, index + 1, is_checked, res);
        //不选
        is_checked[index] = false;//回溯
        Bit_vector(nums, n, index + 1, is_checked, res);
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4};
        Subset subset = new Subset();


        //test 2
        List<List<Integer>> paths = new ArrayList<>();
        Boolean[] is_checked = new Boolean[nums.length];
        subset.Bit_vector(nums, nums.length, 0, is_checked, paths);
    }

}

 三、位向量法改

直接往里面往path里面添加数据,添加后回溯,再递归

public class Subset {
    public void Bit_vector(int[] nums, int n, int index, List<Integer> path, List<List<Integer>> paths) {
        if (index == n) {
            paths.add(new ArrayList<>(path));//复制一遍,不能直接add(path),地址问题
            return;
        }
        //选
        path.add(nums[index]);
        Bit_vector(nums, n, index + 1, path, paths);
        //不选
        //好像可以用removelast,懒得改了
        path.remove(path.size() - 1);//注意: 每次添加的元素是再path的末尾,而不是index位置上
        Bit_vector(nums, n, index + 1, path, paths);
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4};
        Subset subset = new Subset();

        //test 3
        List<List<Integer>> paths = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        subset.Bit_vector(nums, nums.length, 0, new ArrayList<>(), paths);
        System.out.println(paths);
    }

}

四、 二进制法

利用二进制枚举,每个数都只有选和不选两种情况,共 2^{n} 种情况
那我们只需要枚举 [0 , 2^{n}-1] 所有的数即可,利用其来构造子集

0表示选,1表示不选,假如数数组为4,3,2,1,看下面的规律,懂得都懂

0 -> 0000 ->  空集
1 -> 0001 -> {1}
2 -> 0010 -> {2}
3 -> 0011 -> {2,1}
4 -> 0100 -> {3}
5 -> 0101 -> {3,1}
6 -> 0110 -> {3,2}
.....
15 -> 1111 ->{4,3,2,1}
public class Subset {
    public List<List<Integer>> Binary(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        //Bitmask 是用来枚举的数
        //maximum 实际上就是 2^1-1 ,可以用位移运算来计算其值,从而避免Math.pow()后要进行强转
        int i, n = nums.length, Bitmask = 0, maximum = (1 << n) - 1;
        while (Bitmask <= maximum) {
            List<Integer> tmp = new ArrayList<>();
            for (i = 0; i < n; ++i) {
                //注意 从Bitmask的低位开始与 1 进行 '与' 运算,判断条件是大于0
                //如果没有看懂位运算,可以自己写一个函数来进行判断
                //二进制从右向左数第i位是不是1
                if ((Bitmask & (1 << i)) > 0) {
                    tmp.add(nums[i]);
                }
            }
            res.add(tmp);
            ++Bitmask;
        }
        return res;
    }

    public static void main(String[] args) {
        int[] nums = new int[]{1, 2, 3, 4};
        Subset subset = new Subset();

        //test 4
        System.out.println(subset.Binary(nums));
    }


}

参考视频:三种方法求子集 | 小旭讲解 基础算法系列 生成子集 - EP9 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值