子集 - 三种形式

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

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();}
}