题目
https://leetcode-cn.com/problems/subsets/
给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入:nums = [0]
输出:[[],[0]]
解法一(for循环)
假设, 如果已知 [1,2,3]的所有子数组,那么 [1,2,3,4]的所有子数组应该是什么呢?
应该是 [1,2,3]的所有子数组 + 遍历[1,2,3]的所有子数组(每个数组内增加元素4)
//假设, 如果已知 [1,2,3]的所有子数组,那么 [1,2,3,4]的所有子数组应该是什么呢?
//应该是 [1,2,3]的所有子数组 + 遍历[1,2,3]的所有子数组(每个数组内增加元素4)
//根据这个规则写for循环代码
public List<List<Integer>> subsets(int[] nums) {
if (nums == null) {
return null;
}
int length = nums.length;
List<List<Integer>> res = new ArrayList<>();
//先添加一个空数组
res.add(new ArrayList<>());
for (int i = 0; i < length; i++) {
int size = res.size();
for (int j = 0; j < size; j++) {
List<Integer> elementList = new ArrayList<>(res.get(j));
elementList.add(nums[i]);
res.add(elementList);
}
}
return res;
}
解法二(回溯算法)
知乎 回溯算法套路详解
/**
* 方法2
* @param nums
* @return
*/
public static List<List<Integer>> subsets2(int[] nums) {
//结果集
List<List<Integer>> result = new ArrayList();
//临时存放的集合
List<Integer> temp = new ArrayList();
dfs(result,temp,nums,0);
return result;
}
public static void dfs(List<List<Integer>> result, List<Integer> temp, int nums[], int j) {
//添加到结果集中
result.add(new ArrayList<Integer>(temp));
// for 选择 in 选择列表:
// 做选择
// backtrack(路径, 选择列表)
// 撤销选择
for (int i = j; i < nums.length; i++) {
//添加第i个数进入temp中
temp.add(nums[i]);
//将temp添加到结果集中并添加下一个数
dfs(result, temp, nums, i + 1);
Integer lastElement = temp.get(temp.size() - 1);
if (nums[i] == lastElement){
System.out.println("进入dfs之前和退出dfs之后相等" + nums[i]);
}
//把最新添加的一个数删掉继续循环
temp.remove(lastElement);
}//循环完返回上一层递归
精华都在这里,重在理解
以元素[0,1,2,3]举例,第一个路径已经是空集合[], 现在有4个选择,可以选择
num[0]=0;
num[1]=1;
num[2]=2;
num[3]=3。
假设选择了0(如下图),接着可以选择1,2,3。
如果选择1,后面接着可以选择2,3;
如果选择2,后面只能选择3;
如果选择3;后面就没得选
就像是一棵树,上下游走,往下走时,使用的是前序遍历;回溯时,使用的后序遍历。
图中的虚线表示回溯时,走到了该节点,但可以再继续往下走(前序遍历)