78. 子集I和II(回溯+二进制位)

169 阅读1分钟

  • 二进制位
public class Num78子集 {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        //3个数有8种组合,即0-2^3-1,,判断每一个二进位是不是1就选中哪一个数字
        for (int i = 0; i < 1 << nums.length; i++) {
            List<Integer> path = new ArrayList<>();
            for (int j = 0; j < nums.length; j++) {
                //判断i的第j为是不是1
                if ((i >> j & 1) == 1) {
                    path.add(nums[j]);
                }
            }
            ans.add(path);
        }
        return ans;
    }
}
  • 回溯
class Solution {
    // assume no concurrency
    List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> subsets(int[] nums) {
        if (nums.length == 0) {
            return res;
        }
        // 遍历每个位置放什么元素
        dfs(nums, 0, new ArrayList<Integer>());
        return res;
    }

    private void dfs(int[] nums, int start, ArrayList<Integer> path) {
        // 和permutation,combination不一样, 每个中间节点都是答案,都需要存储,所以不能dfs到最下面一层才计算答案
        res.add(new ArrayList<>(path));

        // 尝试每个数字加入当前位子, 有点像combination,取数不能从i=0开始,应该从i=start(上个数字位置的下一个位置)开始选取,才不会重复,比如,选取了1,2,那么之后不能选取2,1
        for (int i = start; i < nums.length; i++) {
            path.add(nums[i]);
            dfs(nums, i + 1, path);
            path.remove(path.size() - 1);
        }
    }
}

  • 子集II(去重)

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    List<Integer> path = new ArrayList<>();

    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums);
        dfs(nums, 0);
        return ans;
    }

    private void dfs(int[] nums, int start) {
        ans.add(new ArrayList<>(path));
        for (int i = start; i < nums.length; i++) {
            //如果当前元素和起始元素重复则跳过
            if(i>start&&nums[i]==nums[i-1]) continue;
            path.add(nums[i]);
            dfs(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}