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],
[]
]
tag: array, backtracking,bit manipulation
method 1
使用回溯/递归的办法,list中每新添加一个数,就将它加入结果集中
subsets里面的循环是寻找递归的起点
find里面的循环是寻找第n层递归的起点,依次从前往后添加元素,以避免重复,第n层递归时,添加长度为n的子集
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
res.add(new ArrayList<>());
for (int i = 0; i < nums.length; i++) {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
res.add(list);
find(list,nums,i+1,res);
}
return res;
}
public void find(List<Integer> list,int[] nums, int start,List<List<Integer>> res){
if (start == nums.length)
return;
for (int i = start; i < nums.length; i++) {
List<Integer> newTmp = new ArrayList<>(list);
newTmp.add(nums[i]);
res.add(newTmp);
find(new ArrayList<>(newTmp),nums,i+1,res);
}
}
method 2
对解法一的优化,这样就少些声明,少用些空间并且简洁,和permutation一样,remove新添加的元素以避免创建新的数组
public List<List<Integer>> subsets2(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
Arrays.sort(nums);
backtrack(list, new ArrayList<>(), nums, 0);
return list;
}
private void backtrack(List<List<Integer>> list , List<Integer> tempList, int [] nums, int start){
list.add(new ArrayList<>(tempList));
for(int i = start; i < nums.length; i++){
tempList.add(nums[i]);
backtrack(list, tempList, nums, i + 1);
tempList.remove(tempList.size() - 1);
}
}
method 3
遍历数组,每次遍历都在前一次迭代的每一个结果后面加上当前遍历的数,当然,加之前要保留之前迭代的每一个结果
public List<List<Integer>> subsets3(int[] nums) {
List<List<Integer>> list = new ArrayList<>();
list.add(new ArrayList<>());
for (int num:nums) {
int n = list.size();
for (int i = 0; i < n; i++) {
List<Integer> newList = new ArrayList<>(list.get(i));
newList.add(num);
list.add(newList);
}
}
return list;
}
method 4
位操作
/**
* 位操作,没太看得懂,留着
*https://leetcode.com/problems/subsets/discuss/27278/C%2B%2B-RecursiveIterativeBit-Manipulation
* @param nums
* @return
*/
public List<List<Integer>> subsets4(int[] nums) {
int n = nums.length;
List<List<Integer>> subsets = new ArrayList<>();
for (int i = 0; i < Math.pow(2, n); i++)
{
List<Integer> subset = new ArrayList<>();
for (int j = 0; j < n; j++)
{
int tmp = (i >> j);
int res = tmp & 1;
if (res != 0)
subset.add(nums[j]);
}
Collections.sort(subset);
subsets.add(subset);
}
return subsets;
}
summary:
- 对得到所有子集/所有排列的递归总结
- 递归内的循环是找第n层的的起点
- 为了避免重复,要严格从左到右遍历
- 对得到所有自己/所有排列,也可以递归处理,每次都对所有的list添加当前数