子集

子集

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: nums = [1,2,3]
输出:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

方法一:回溯

public List> subsets(int[] nums) {
    List> ans=new ArrayList<>();
    Stack stack=new Stack<>();
    dfs(0,nums,ans,stack);
    return ans;
}

public void dfs(int depth,int[] nums,List> ans,Stack stack) {
    if(depth==nums.length){
        ans.add(new ArrayList<>(stack));
    } else {
        dfs(depth+1,nums,ans,stack);
        stack.push(nums[depth]);
        dfs(depth+1,nums,ans,stack);
        stack.pop();
    }
}

时间复杂度(n×2n)
空间复杂度(n×2n)

方法二:回溯2
但注意子集2不能用这种方法的set去重

private List> res = new ArrayList<>();

public List> subsets(int[] nums) {
    dfs(nums, 0, new Stack<>());
    return res;
}

private void dfs(int[] nums, int i, Stack stack) {
    res.add(new ArrayList<>(stack));
    for (int j = i; j < nums.length; j++) {
        stack.push(nums[j]);
        dfs(nums, j + 1, stack);
        stack.pop();
    }
}

方法三:迭代
最初结果为[]
在遍历nums数组过程中,将结果中的元素取出,在它们后面都加上当前元素

如对于 1 2 3
最初      []
i=0       [],1
i=1       [],1,2,12
i=2       [],1,2,12,3,13,23,123
public List> subsets(int[] nums) {
    List> ans=new ArrayList<>();
    ans.add(new ArrayList<>());
    for (int i = 0; i < nums.length; i++) {
        int size=ans.size();
        for (int j=0;j list = new ArrayList<>(ans.get(j));
            list.add(nums[i]);
            ans.add(list);
        }
    }
    return ans;
}

时间复杂度O(n×2n):一共与2n个子集,每个又复制到结果中
空间复杂度O(n×2n)

方法四:迭代利用二进制
原序列中的每个数字ai的状态可能有两种,即「在子集中」和「不在子集中」。我们用 1 表示「在子集中」,0 表示不在子集中,那么每一个子集可以对应一个长度为 n 的 0/1 序列,第i位表示 ai是否在子集中。例如,n = 3,a={5,2,9} 时:

0/1 序列   子集   0/1序列对应的二进制数
000         {}              0
001         {9}             1
010         {2}             2
011         {2,9}           3
100         {5}             4
101         {5,9}           5
110         {5,2}           6
111         {5,2,9}         7

可以发现 0/1 序列对应的二进制数正好从 0 到 2n - 1

public List> subsets(int[] nums) {
    List> res = new ArrayList<>();
    int n = nums.length;
    for (int i = 0; i < (1 << n); i++) {
        List list = new ArrayList<>();
        for (int j = 0; j < n; j++) {
            if ((i & (1 << j)) != 0) {
                list.add(nums[j]);
            }
        }
        res.add(list);
    }
    return res;
}

子集 II

给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:

输入: [1,2,2]
输出:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

剪枝
方法一:含有标记数组的回溯
先分析元素不在子集中,再分析在子集中

public List> subsetsWithDup(int[] nums) {
    List> ans = new ArrayList<>();
    boolean[] used=new boolean[nums.length];
    Arrays.sort(nums);
    dfs(0, nums, ans, used);
    return ans;
}


public void dfs(int depth, int[] nums, List> ans, boolean[] used) {
    if (depth == nums.length) {
        ArrayList list = new ArrayList<>();
        for (int i = 0; i < used.length; i++) {
            if(used[i]){
                list.add(nums[i]);
            }
        }
        ans.add(list);
    } else {
        dfs(depth + 1, nums, ans, used);
        if(depth>0&&nums[depth]==nums[depth-1]&&!used[depth-1]){
            return;
        }
        used[depth]=true;
        dfs(depth + 1, nums, ans, used);
        used[depth]=false;
    }
}

另外可以不需要used数组,只需要一个变量记录是否有选前一个元素

private List> lists = new ArrayList<>();

public List> subsetsWithDup(int[] nums) {
    Arrays.sort(nums);
    dfs(nums, 0, new Stack<>(), false);
    return lists;
}

private void dfs(int[] nums, int i, Stack stack, boolean used) {
    if (i == nums.length) {
        lists.add(new ArrayList<>(stack));
        return;
    }
    dfs(nums, i + 1, stack, false);
    if (i > 0 && nums[i] == nums[i - 1] && !used) {
        return;
    }
    stack.add(nums[i]);
    dfs(nums, i + 1, stack, true);
    stack.pop();
}

方法二:不含标记,手工回溯
先分析元素在子集中,再分析在不子集中

public List> subsetsWithDup(int[] nums) {
    List> ans = new ArrayList<>();
    Stack stack = new Stack<>();
    Arrays.sort(nums);
    dfs(0, nums, ans, stack);
    return ans;
}

public void dfs(int depth, int[] nums, List> ans, Stack stack) {
    if (depth == nums.length) {
        ans.add(new ArrayList<>(stack));
    } else {
        stack.push(nums[depth]);
        dfs(depth + 1, nums, ans, stack);
        stack.pop();
        while (depth+1 < nums.length && nums[depth] == nums[depth + 1]) {
            depth++;
        }
        dfs(depth + 1, nums, ans, stack);
    }
}

方法三:迭代

public List> subsetsWithDup(int[] nums) {
    List> res = new ArrayList<>();
    int n = nums.length;
    Arrays.sort(nums);
    for (int i = 0; i < (1 << n); i++) {
        List list = new ArrayList<>();
        boolean flag = false;
        for (int j = 0; j < n; j++) {
            if ((i & (1 << j)) != 0) {
                if (j > 0 && nums[j] == nums[j - 1] && (i & (1 << (j - 1))) == 0) {
                    flag = true;
                    break;
                }
                list.add(nums[j]);
            }
        }
        if (!flag) {
            res.add(list);
        }
    }
    return res;
}

你可能感兴趣的:(子集)