递归与回溯
-
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ] LeetCode 131
class Solution { public List<List<String>> partition(String s) { List<List<String>> result=new ArrayList<>(); DFS(result,s,new ArrayList<String>()); return result; } public void DFS(List<List<String>> result,String s,List<String> temp){ if(s.length()==0){ //每次需要新new一个ArrayList 把temp赋值过去 //不能直接使用temp temp还会因为回溯动态变化 result.add(new ArrayList<String>(temp)); return; } for(int i=1;i<=s.length();i++){ if(isPalindrome(s.substring(0,i))){ temp.add(s.substring(0,i)); DFS(result,s.substring(i,s.length()),temp); //回溯 temp.remove(temp.size()-1); } } } public boolean isPalindrome(String s){ for(int l=0,r=s.length()-1;l<r;l++,r--){ if(s.charAt(l)!=s.charAt(r)){ return false; } } return true; } }
-
给定一个 没有重复 数字的序列,返回其所有可能的全排列。
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] leetcode -- 46
class Solution { public List<List<Integer>> permute(int[] nums) { List<List<Integer>> result=new ArrayList<>(); boolean[] used=new boolean[nums.length]; return DFS(result,nums,used,new ArrayList<>()); } public List<List<Integer>> DFS(List<List<Integer>> result,int[] nums,boolean[] used,List<Integer> temp){ //表明temp添加完了所有数 递归出口 if(temp.size()==nums.length){ result.add(new ArrayList<>(temp)); return result; } for(int i=0;i<nums.length;i++){ if(used[i]!=true){ //temp在动态保存添加过程中的结果值 temp.add(nums[i]); used[i]=true; DFS(result,nums,used,temp); used[i]=false; temp.remove(temp.size()-1); } } return result; } }
-
给定一个可包含重复数字的序列,返回所有不重复的全排列。
输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ] leetcode - 47
class Solution { public List<List<Integer>> permuteUnique(int[] nums) { //排序 Arrays.sort(nums); List<List<Integer>> result=new ArrayList<>(); boolean[] used=new boolean[nums.length]; return DFS(result,nums,used,new ArrayList<>()); } public List<List<Integer>> DFS(List<List<Integer>> result,int[] nums,boolean[] used,List<Integer> temp){ if(temp.size()==nums.length){ result.add(new ArrayList<>(temp)); return result; } for(int i=0;i<nums.length;i++){ if(used[i]!=true){ //剪枝 //注意used[i-1]==false 表示nums[i-1]刚刚被撤销 这种就需要剪枝 //如果used[i-1]!=false 表示nums[i]在正常添加过程中 如果剪枝反而错误 if(i>0&&nums[i]==nums[i-1]&&!used[i-1]) continue; temp.add(nums[i]); used[i]=true; DFS(result,nums,used,temp); used[i]=false; temp.remove(temp.size()-1); } } return result; } }
-
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
leetcode -77
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> result=new ArrayList<>();
int[] nums=new int[n];
for(int i=0;i<n;i++){
nums[i]=i+1;
}
return DFS(result,k,new ArrayList<Integer>(),0,nums);
}
public List<List<Integer>> DFS(List<List<Integer>> result,int k,ArrayList<Integer> temp,int index,int[] nums){
if(temp.size()==k){
result.add(new ArrayList(temp));
return result;
}
for(int i=index;i<nums.length;i++){
temp.add(nums[i]);
//这里下一次DFS的时候 index赋值为i+1 保证已经选出的元素不在出现
DFS(result,k,temp,i+1,nums);
temp.remove(temp.size()-1);
}
return result;
}
}
-
给定一个无重复元素的数组
candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。输入: candidates = [2,3,5], target = 8, 所求解集为: [ [2,2,2,2], [2,3,3], [3,5] ] 所有数字(包括 `target`)都是正整数。 解集不能包含重复的组合。 (LeetCode -- 39)
class Solution { public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result=new ArrayList<>(); return DFS(candidates,result,new ArrayList<Integer>(),target,0); } public List<List<Integer>> DFS(int[] candidates,List<List<Integer>> result,List<Integer> temp,int target,int start){ if(target<0) return result; if(target==0){ result.add(new ArrayList<>(temp)); return result; } //对于解集不能包含重复的组合问题 循环从start开始 保证解集中没有重复 for(int i=start;i<candidates.length;i++){ temp.add(candidates[i]); //这里下一次DFS的start赋值为i 是因为解中可以出现2 2 2 。。这样重复元素的情况 DFS(candidates,result,temp,target-candidates[i],i); temp.remove(temp.size()-1); } return result; } }
-
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ] (LeetCode --40)
class Solution { public List<List<Integer>> combinationSum2(int[] candidates, int target) { //先排序 Arrays.sort(candidates); List<List<Integer>> result=new ArrayList<>(); boolean[] used=new boolean[candidates.length]; return DFS(candidates,result,new ArrayList<Integer>(),target,used,0); } public List<List<Integer>> DFS(int[] candidates,List<List<Integer>> result,List<Integer> temp,int target,boolean[] used,int start){ if(target<0) return result; if(target==0){ result.add(new ArrayList<>(temp)); return result; } //定义start索引 解决解集不能包含重复的组合 for(int i=start;i<candidates.length;i++){ if(!used[i]){ //剪枝 给定的样例中包含重复元素 为了解集不能包含重复的组合 剪枝 if(i>0&&candidates[i]==candidates[i-1]&&!used[i-1]) continue; temp.add(candidates[i]); used[i]=true; DFS(candidates,result,temp,target-candidates[i],used,i+1); used[i]=false; temp.remove(temp.size()-1); } } return result; } }
-
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明: 所有数字都是正整数。 解集不能包含重复的组合。 ```xml 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] ``` leetcode 216
class Solution { public List<List<Integer>> combinationSum3(int k, int n) { List<List<Integer>> result=new ArrayList<>(); int[] nums=new int[9]; for(int i=0;i<9;i++){ nums[i]=i+1; } return DFS(result,nums,new ArrayList<>(),n,k,0); } public List<List<Integer>> DFS(List<List<Integer>> result,int[] nums,List<Integer> temp,int n,int k,int start){ if(n<0){ return result; } //递归终止条件有2个 temp的大小为3 且这3个数和为给定的值 if(temp.size()==k&&n==0){ result.add(new ArrayList<>(temp)); return result; } //解集不能包含重复的组合问题 从start开始循环 不每次从0开始 for(int i=start;i<nums.length;i++){ temp.add(nums[i]); DFS(result,nums,temp,n-nums[i],k,i+1); temp.remove(temp.size()-1); } return result; } }
-
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ] 来源:力扣(LeetCode--78)
class Solution { public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> result=new ArrayList<>(); //对元素个数k做一次循环 这个k对应了DFS时候的终止条件 for(int k=0;k<=nums.length;k++){ DFS(result,new ArrayList<>(),k,nums,0); } return result; } public void DFS(List<List<Integer>> result,List<Integer> temp,int k,int[] nums,int start){ if(temp.size()==k){ result.add(new ArrayList<>(temp)); return; } for(int i=start;i<nums.length;i++){ temp.add(nums[i]); DFS(result,temp,k,nums,i+1); temp.remove(temp.size()-1); } return; } }
-
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
**说明:**解集不能包含重复的子集。
输入: [1,2,2] 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ] leetcode -90
class Solution { public List<List<Integer>> subsetsWithDup(int[] nums) { Arrays.sort(nums); List<List<Integer>> result=new ArrayList<>(); boolean[] flags=new boolean[nums.length]; for(int k=0;k<=nums.length;k++){ DFS(result,new ArrayList<>(),k,nums,0,flags); } return result; } public void DFS(List<List<Integer>> result,List<Integer> temp,int k,int[] nums,int start,boolean[] flags){ if(temp.size()==k){ result.add(new ArrayList<>(temp)); return; } for(int i=start;i<nums.length;i++){ if(!flags[i]){ //给定输入样例中包含重复元素 就要先排序 后剪枝 if(i>0&&nums[i]==nums[i-1]&&!flags[i-1]) continue; temp.add(nums[i]); flags[i]=true; DFS(result,temp,k,nums,i+1,flags); flags[i]=false; temp.remove(temp.size()-1); } } return; } }