1. 所有子集
题目:给定一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
题解:求子集,且互不相同,每次dfs可以选择取当前数字取还是不取,当执行到最后一个元素时,答案终止。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
public void dfs(int[] nums, List<Integer> tmp,int i){
if(i==nums.length){
ans.add(new ArrayList<Integer>(tmp));
return;
}
tmp.add(nums[i]);
dfs(nums,tmp,i+1);
tmp.remove(tmp.size()-1);
dfs(nums,tmp,i+1);
}
public List<List<Integer>> subsets(int[] nums) {
List<Integer> tmp = new ArrayList<>();
dfs(nums,tmp,0);
return ans;
}
}
2. 子集 II
题目:给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
题解:与上一道题不同,这道题有重复元素,所以需要保证在同一层的时候,有重复元素跳过
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public void dfs(int[] nums,int i){
if(i==nums.length){
ans.add(new ArrayList<Integer>(tmp));
return;
}
tmp.add(nums[i]);
dfs(nums,i+1); //往下取,不在同一层,无须判重
tmp.remove(tmp.size()-1);
while(i+1<nums.length&&nums[i+1]==nums[i]) //取当前层的下一个不重复的数
i++;
dfs(nums,i+1);
}
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
dfs(nums,0);
return ans;
}
}
3. 含有 k 个元素的组合
题目:给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
题解:不含重复元素,与子集问题的入口不同
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public void dfs(int n,int k,int cur,int cnt){
if(cur>n+1) return;
if(cnt==k){
ans.add(new ArrayList<Integer>(tmp));
return;
}
tmp.add(cur);
dfs(n,k,cur+1,cnt+1);
tmp.remove(tmp.size()-1);
dfs(n,k,cur+1,cnt);
}
public List<List<Integer>> combine(int n, int k) {
dfs(n,k,1,0);
return ans;
}
}
4. 组合总和
题目:给定一个无重复元素的正整数数组 candidates 和一个正整数 target ,找出 candidates 中所有可以使数字和为目标数 target 的唯一组合。candidates 中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是不同的。 对于给定的输入,保证和为 target 的唯一组合数少于 150 个。
题解:与上一题相比,在于在进下一层的时候,可以从当前元素开始
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public void dfs(int[] candidates, int target,int i,int sum){
if(sum>=target){
if(sum==target)
ans.add(new ArrayList<Integer>(tmp));
return;
}
if(i==candidates.length){
return;
}
tmp.add(candidates[i]);
dfs(candidates,target,i,sum+candidates[i]);//这里有区别
tmp.remove(tmp.size()-1);
dfs(candidates,target,i+1,sum);
}
public List<List<Integer>> combinationSum(int[] candidates, int target) {
dfs(candidates,target,0,0);
return ans;
}
}
5. 组合总和 II
题目:给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用 一次 。注意:解集不能包含重复的组合。
题解:与上一道题有两个不同,第一,每个数字只能使用一次,第二,数组中包含重复元素
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public void dfs(int[] candidates, int target,int i,int sum){
if(sum>=target){
if(sum==target)
ans.add(new ArrayList<Integer>(tmp));
return;
}
if(i==candidates.length){
return;
}
tmp.add(candidates[i]);
dfs(candidates,target,i+1,sum+candidates[i]);//第一个差异
tmp.remove(tmp.size()-1);
while(i+1<candidates.length&&candidates[i+1]==candidates[i]){//第二个差异
i++;
}
dfs(candidates,target,i+1,sum);
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0,0);
return ans;
}
}
这种方式与上一个类似,只不过换成了循环的方式
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
public void dfs(int[] candidates, int target,int index,int sum){
if(target<sum) return;
if(target==sum)
ans.add(new ArrayList<Integer>(tmp));
for(int i = index;i<candidates.length;i++){//i=index 保证下面一层取,从index开始
if(i>index&&candidates[i]==candidates[i-1])//保证在同一级递归中两个数不重复
continue;
tmp.add(candidates[i]);
dfs(candidates,target,i+1,sum+candidates[i]);
tmp.remove(tmp.size()-1);
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0,0);
return ans;
}
}
6. 没有重复元素集合的全排列
题目:给定一个不含重复数字的整数数组 nums ,返回其 所有可能的全排列 。可以 按任意顺序 返回答案。
题解:全排列问题,每一次可以选择所有元素,不需要按照数组顺序,需要用一个数组去重
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
Map<Integer,Integer> mp = new HashMap<Integer,Integer>();
public void dfs(int[] nums){
if(tmp.size()==nums.length){
ans.add(new ArrayList<Integer>(tmp));
return;
}
for(int i=0;i<nums.length;i++){
if(mp.get(nums[i])!=null)
continue;
tmp.add(nums[i]);
mp.put(nums[i],1);
dfs(nums);
tmp.remove(tmp.size()-1);
mp.remove(nums[i]);
}
}
public List<List<Integer>> permute(int[] nums) {
dfs(nums);
return ans;
}
}
7. 含有重复元素集合的全排列
题目:给定一个可包含重复数字的整数集合 nums ,按任意顺序 返回它所有不重复的全排列。
题解:与上一题相比,含有重复元素,需要再每一层的时候取到不相同的元素,用
(i>0&&nums[i]==nums[i-1]&&mp.get(i-1)!=null) 保证
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> tmp = new ArrayList<>();
Map<Integer,Integer>mp = new HashMap<Integer,Integer>();
public void dfs(int[] nums){
if(tmp.size()==nums.length){
ans.add(new ArrayList<Integer>(tmp));
return;
}
for(int i=0;i<nums.length;i++){
//如果当前位置使用过,则跳过
//如果当前位置没有使用过,但是当前元素等于前一个元素,且前一个元素,则跳过
if(mp.get(i)!=null||(i>0&&nums[i]==nums[i-1]&&mp.get(i-1)!=null)) continue;
tmp.add(nums[i]);
mp.put(i,1);
dfs(nums);
tmp.remove(tmp.size()-1);
mp.remove(i);
}
}
public List<List<Integer>> permuteUnique(int[] nums) {
Arrays.sort(nums);
dfs(nums);
return ans;
}
}