题目链接:
https://leetcode.com/problems/subsets/description/
题目描述:
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
Input: nums = [1,2,3]
Output:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
这个题目是在n个数中选(0到n)个数有多少种不同的方法,其实还是选或不选
的问题,和77. Combinations本质上是一样的,而且笔者之前在这篇文章中总结了此类问题的套路,读者可以点进去看看。
下边是这个问题的解法
代码一
相关知识: 排列公式Anm是指从n个元素取m个进行排列(即排序)
组合公式Cnm是指从n个元素取m个不进行排列(即不排序)
这个相当于是组合公式,由例子来说,相当于是从3个数取数,由多少种取法,这里是2的3次方,相当于8种方法。如果输入为N个元素,则有2的n次方中方法。
该题相当于顺序遍历该列表中的元素,每个元素取或者不取。下图是该问题的图解,每个二叉树的分支,左孩子表示选择该元素,有孩子表示不选。
代码如下:
class Solution {
public List<List<Integer>> subsets(int[] nums) {
if(nums == null || nums.length == 0) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> ans = new ArrayList<>();
backtrack(ans, new ArrayList<Integer>(), nums, 0);
return ans;
}
private void backtrack(List<List<Integer>> ans, List<Integer> list, int[] nums, int index) {
if(index >= nums.length) {
ans.add(new ArrayList<Integer>(list));
return;
}
list.add(nums[index]);
backtrack(ans, list, nums, index+1);
list.remove(list.size() - 1);
backtrack(ans, list, nums, index+1);
return;
}
}
代码二
上述代码结构笔者比较喜欢的回溯方法,因为比较容易理解,下边的代码是笔者之前经常弄混的回溯法的另一种方法,下边的代码在leecode上提交之后也AC,其实代码一和代码二非常的相似,但是有下边几点不同:
- 代码二的for循环里,只有一个回溯,而代码一里有两个回溯。
- 代码二每次回溯的开头就将答案保存,而代码一需要在结尾的时候保存答案。
为什么代码二里有两个回溯,而代码一里只有一个回溯呢?因为代码二是一个for循环,这个循环相当于遍历了树的每一层,因此remove之后不用再回溯,相当于这一层不选,然后for至下一层,再判断是否选择。
个人推荐代码一,好懂!
class Solution {
public List<List<Integer>> subsets(int[] nums) {
if(nums == null || nums.length == 0) {
return new ArrayList<List<Integer>>();
}
List<List<Integer>> ans = new ArrayList<>();
backtrack(ans, new ArrayList<Integer>(), nums, 0);
return ans;
}
private void backtrack(List<List<Integer>> ans, List<Integer> list, int[] nums, int index) {
ans.add(new ArrayList<Integer>(list));
for(int i = index; i < nums.length; i++) {
list.add(nums[i]);
backtrack(ans, list, nums, i+1);
list.remove(list.size() - 1);
}
return;
}
}