题目
输入一个不含重复数字的数据集合,请找出它的所有子集。例如,数据集合[1,2]有4个子集,分别是[]、[1]、[2]和[1,2]。
分析
如果集合中包含n个元素,那么生成子集可以分为n步,每一步从集合中取出一个数字,此时面临两个选择,将该数字添加到子集中或不将该数字添加到子集中。生成一个子集可以分为若干步,并且每一步都面临若干选择,这正是应用回溯法的典型场景。
适用回溯法的问题的一个特征是问题可能有很多个解,并且题目要求列出所有的解。如果题目只是要求计算解的数目,或者只需要求一个最优解(通常是最大值或最小值),那么可能需要运用动态规划。
解
public class Test {public static void main(String[] args) {int[] nums = {1, 2};List<List<Integer>> result = subsets(nums);for (List<Integer> item : result) {System.out.println(item);}}public static List<List<Integer>> subsets(int[] nums) {List<List<Integer>> result = new LinkedList<>();if (nums.length == 0) {return result;}helper(nums, 0, new LinkedList<Integer>(), result);return result;}private static void helper(int[] nums, int index, LinkedList<Integer> subset, List<List<Integer>> result) {if (index == nums.length) {result.add(new LinkedList<>(subset));}else if (index < nums.length) {helper(nums, index + 1, subset, result);// 直接不添加元素subset.add(nums[index]);// 添加元素helper(nums, index + 1, subset, result);// 等递归函数执行完成之后,函数helper也执行完成,接下来将回到前一个数字的函数调用处继续执行。那么此时将回溯到父节点,以便尝试父节点的其他选项。// 在回溯到父节点之前,应该清除已经对子集状态进行的修改。此前在子集subset中添加了一个数字,此时应该将它删除。subset.removeLast();}}
}