Question
Given a set of distinct integers, nums, return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,3]
, a solution is:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
本题难度Medium。
【复杂度】
时间 O(N) 空间 O(N)
【思路】
与[LeetCode]Combinations的思路一样,不同点就在于:
- 19行,将递归过程中的preList放入结果
- base case仅仅判断是否到达末端
【代码】
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
//require
List<List<Integer>> ans=new LinkedList<>();
ans.add(new LinkedList<Integer>()); //add [] to ans
//invariant
helper(0,new LinkedList<Integer>(),nums,ans);
//ensure
return ans;
}
private void helper(int start,List preList,int[] nums,List<List<Integer>> ans){
int size=nums.length;
//base case
if(start==size)
return;
for(int i=start;i<size;i++){
preList.add(nums[i]);
ans.add(new LinkedList(preList));
helper(i+1,preList,nums,ans);
preList.remove(preList.size()-1);
}
}
}
【注意】
[]
也是一个subSet。
follow up
我想起来Stanford的公开课《Programming Abstractions》第10集对此有讲述,她的方法更为精妙:
每次分为两种情况:
一种是包含该元素,一种是不包含该元素
一并解决了[]
也是subSet的问题。
代码:
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
//require
List<List<Integer>> ans=new LinkedList<>();
//invariant
helper(0,new LinkedList<Integer>(),nums,ans);
//ensure
return ans;
}
private void helper(int start,List preList,int[] nums,List<List<Integer>> ans){
int size=nums.length;
//base case
if(start==size){
ans.add(new LinkedList(preList));
return;
}
//include nums[start]
preList.add(nums[start]);
helper(start+1,preList,nums,ans);
preList.remove(preList.size()-1);
//exclude nums[start]
helper(start+1,preList,nums,ans);
}
}
看到代码的22行会有点奇怪:为什么它后面没有preList.remove(preList.size()-1);
。(长期做recursition的职业病)能这样做的原因就在于:向右遍历时并不破坏初始状态。比如:
集合为:abcd
当preList=“abc”时,preList在遍历树中是这样的:
|
第n级 abc
| |
第n+1级 abcd abc
由第n级向第n+1级时,先下到左边,preList=“abcd”
,然后递归回第n级时,删掉'd'
,preList又变回"abc"
,然后再下到右边preList不变,然后递归回第n级,preList仍为初始状态。