「这是我参与2022首次更文挑战的第28天,活动详情查看:2022首次更文挑战」。
题目:给定一个整数数组,数组中的元素不存在相同的两个。要求返回该数组所有可能的子集。子集中不可包含重复的子集,可按照任意顺序返回。
解题思路
回想LeetCode 46全排列那题,那题是得到所有的全排列,解决思路是构建一个递归树,一次次递归,最终将所有的叶子结点的值作为结果,要求所有返回的值不重复,因此那题是采用visit数组来将每次访问到的元素进行标记。再看LeetCode 39求所有的组合数,也是要求结果不能重复,此处使用的则是每次递归设置一个起始点,递归中从这个起始点开始求解。其要得到的是符合条件的叶子和中间结果。
本题和上面两题有点类似,其最终目的是寻找树的所有结点。我们可以想象,在每次构建树的时候,我们都将结点加入一个新的容器,最终这个容器的总和不就是我们的答案。并且因为我们要求元素不重复,那可以为数组设置一个起始点,起始点保证了最终结果的唯一性,代码如下:
ArrayList<List<Integer>> res = new ArrayList<>(); // 总容器
ArrayList<Integer> temp = new ArrayList<>(); //中间容器
public List<List<Integer>> subsets(int[] nums) {
dfs(nums, 0);
return res;
}
public void dfs(int[] nums, int start){
res.add(new ArrayList<>(temp));
for(int i=start;i<nums.length;i++){
temp.add(nums[i]);
dfs(nums, i+1);
temp.remove(temp.size()-1);
}
}
上述代码就是经典的回溯,回溯终止条件即为判断当前起始点是否大于数组的长度,如果大于则不会继续回溯下去。
思路二
从前往后遍历数组,每当遇到数组中的一个数,就将该数和已有的所有子集进行组合,形成新的子集,这样当数组遍历完毕,子集也计算完毕,代码如下:
public List<List<Integer>> subsets2(int[] nums) {
ArrayList<List<Integer>> res2 = new ArrayList<>();
res2.add(new ArrayList<>());
for(int i=0;i<nums.length;i++){
int size = res2.size();
for(int j=0;j<size;j++){
ArrayList<Integer> temp2 = new ArrayList<>(res2.get(j));
temp2.add(nums[i]);
res2.add(temp2);
}
}
return res2;
}
上述方法的时间复杂度为, 空间复杂度为。