78. 子集
思路:
做法和组合的题目类似,差别在于多了不同长度的子集,对于不同长度的子集,可分别通过多次调用递归,也就是遍历长度为1,2,3,4等不同长度的子集。其中顺序无关这个其实就要求在递归过程中,防止重复遍历,具体解析见递归函数。
private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
//遍历长度为0、1、2、3...的子集
int len = nums.length;
boolean[] hash = null;
int[] tmp = null;
for(int l = 0 ; l <= len; l++) {
hash = new boolean[len];
tmp = new int[l];
digui(nums,hash,tmp,0,l,-1); //注意prevIndex得小于0
}
return res;
}
由于子集元素是不能重复的,所以需要防止重复遍历,例如123,321是一样的子集。我们以[1,2,3]为例,长度为2的子集可以为12,13,23,而我们想避免全排列的21,31,32,观察12,13,23这些子集,它们的特点都是后一个数的下标比前一个大递归时。因此递全排列时,只需保证当前元素的下标大于前一个元素的下标(prevIndex作用),这里不能用元素的值,因为nums数组不一定有序,例如[3,2,1]。其处理做法跟组合是一样的。
//index代表第几层,len代表子集长度,prevIndex代表上一个元素的下标
public void digui(int[] nums, boolean[] hash, int[] tmp, int index, int len, int prevIndex){
if(index == len) {
List<Integer> list = new ArrayList<>();
for(Integer a : tmp) {
list.add(a);
}
res.add(list);
return;
}
int l = nums.length;
for(int i=0; i < l; i++) {
//防止重复遍历
if(i > prevIndex) {
if(hash[i] == false) {
tmp[index] = nums[i];
hash[i] = true;
digui(nums, hash, tmp, index+1, len, i);
hash[i] = false;
}
}
}
}
90. 子集 II
https://leetcode.cn/problems/subsets-ii/
包含重复的集合,求子集
class Solution {
private List<List<Integer>> res;
private List<Integer> zj; //存放子集
public List<List<Integer>> subsetsWithDup(int[] nums) {
res = new ArrayList<>();
res.add(new ArrayList<>());
int l = nums.length;
zj = new ArrayList<>(l);
//排序
Arrays.sort(nums);
for(int len = 1; len <= l; len++) {
dfs(len, nums, 0);
}
return res;
}
public void dfs(int len, int[] nums, int prev) {
if(len == 0) {
//注意需要new
res.add(new ArrayList<>(zj));
return;
}
int l = nums.length;
for(int i = prev; i < l; i++) {
//避免重复访问
if(i > prev && nums[i] == nums[i-1]) continue;
zj.add(nums[i]);
dfs(len - 1, nums, i + 1);
zj.remove(zj.size() - 1);
}
}
}