子集 - 三种形式

无重不可复选,可重不可复选,无重可复选

78. 子集 - 元素无重不可复选

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

题解

通过保证元素之间的相对顺序不变来防止出现重复的子集

注意:把根节点当作第 0 层,第 n 层的所有节点就是节点数目为 n 的所有子集。

  1. 使用 start 避免产生重复的子集。
  2. 使用 track 记录根到每个节点的路径。
  3. 前序位置收集所有子集。
public List<List<Integer>> res = new LinkedList<>(); // 结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录路径
public List<List<Integer>> subsets(int[] nums) {
    backtrack(nums, 0);
    return res;
}
// 回溯算法,遍历子集问题的回溯树
public void backtrack(int[] nums, int start) {
    // 前序遍历,记录所有节点的值,每个节点的值都是一个子集
    res.add(new LinkedList<>(track));
    // 回溯框架
    for (int i = start; i < nums.length; i++) { //当 start == nums.length 时,叶子节点的值会被装入 res,但 for 循环不会执行,也就结束了递归。
        // 做选择
        track.add(nums[i]);
        // 只选择start之后的值,通过 start 参数控制树枝的遍历,避免产生重复的子集
        backtrack(nums, i + 1);
        // 撤销选择
        track.removeLast();
    }
}

90. 子集 II - 元素可重不可复选

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

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

题解

剪枝

  1. 排序让相同元素在一起
  2. 如果发现重复值: nums[i] == nums[i-1],跳过
public List<List<Integer>> res = new LinkedList<>(); // 结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录路径
public List<List<Integer>> subsetsWithDup(int[] nums) {
    // 先排序,让相同的元素靠在一起
    Arrays.sort(nums);
    backtrack(nums, 0);
    return res;
}
/**
 * 剪枝,值相同的相邻树枝(跳过),只遍历第一个
 */
public void backtrack(int[] nums, int start) {
    // 前序位置,每个节点的值都是一个子集
    res.add(new LinkedList<>(track));
    for (int i = start; i < nums.length; i++) {
        // 剪枝,同一层,发现重复值,跳过
        if (i > start && nums[i] == nums[i - 1]) continue;
        track.add(nums[i]);
        backtrack(nums, i + 1);
        track.removeLast();
    }
}

子集 III - 元素无重可复选

通过start参数,控制树枝的遍历

  1. start = start,下一层回溯树,可以选自己(一个元素可以无限次使用)
  2. start = start + 1,下一层回溯树,不可以选自己(一个元素只使用一次)
public List<List<Integer>> res = new LinkedList<>(); // 记录结果
public LinkedList<Integer> track = new LinkedList<>(); // 记录回溯路径
public List<List<Integer>> subsets(int[] nums) {
    backtrack(nums, 0);
    return res;
}
// 回溯算法
public void backtrack(int[] nums, int start) {
    // 前序位置,每个节点的值都是一个子集
    if (track.size() == nums.length) {
        res.add(new LinkedList<>(track));
        return;
    }
    for (int i = start; i < nums.length; i++) {
        track.add(nums[i]);
        // 通过start参数,控制树枝的遍历
        // start = start,下一层回溯树,可以选自己(一个元素可以无限次使用)
        // start=start+1,下一层回溯树,不可以选自己(一个元素只使用一次)
        backtrack(nums, i);
        track.removeLast();
    }
}

你可能感兴趣的:(算法,算法,java)