写在前面:
求解组合问题常用的方法是回溯,回溯类似于DFS(深度优先遍历),实际是一个暴力搜索的过程,把所有的组合都列举出来,找出符合题意的组合。
1、Leetcode 17. Letter Combinations of a Phone Number
思路:
参考高赞回答。
class Solution {
public List<String> letterCombinations(String digits) {
List<String> res=new ArrayList<>();
if(digits==null||digits.length()==0){
return res;
}
char[][] map={{},{},{'a','b','c'},{'d','e','f'},{'g','h','i'},{'j','k','l'},{'m','n','o'},{'p','q','r','s'},{'t','u','v'},{'w','x','y','z'}};
backtrack(digits,new StringBuilder(),map,0,res);
return res;
}
public void backtrack(String digits,StringBuilder sb,char[][] map,int start,List<String > res){
if(start==digits.length()){
res.add(new String(sb));
//因为StringBuilder传的是引用,所以要new一个来传值
return;
}
int num=digits.charAt(start)-'0';
for(int i=0;i<map[num].length;i++){
sb.append(map[num][i]);
backtrack(digits,sb,map,start+1,res);
sb.deleteCharAt(sb.length()-1);
}
}
}
2、39. Combination Sum
思路:
参考A general approach to backtracking questions in Java (Subsets, Permutations, Combination Sum, Palindrome Partitioning),这篇高赞回答非常棒,总结了回溯问题的通用解法。
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
backtrack(candidates,res,new ArrayList<>(),0,target,0);
return res;
}
public void backtrack(int[] candidates,List<List<Integer>> res,List<Integer> list,int curSum,int target,int start){
if(curSum>target){
return;
}
if(curSum==target){
res.add(new ArrayList(list));
return;
}
for(int i=start;i<candidates.length;i++){//i从start开始,只往前不往后
list.add(candidates[i]);
backtrack(candidates,res,list,curSum+candidates[i],target,i);
//不是i+1因为一个元素可以取多次
list.remove(list.size()-1);
}
}
}
3、40. Combination Sum II
思路
剪枝参考高赞回答。
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
Arrays.sort(candidates);//元素相同顺序不同只能视为一种解法
backtrack(candidates,res,new ArrayList<>(),0,target,0);
return res;
}
public void backtrack(int[] candidates,List<List<Integer>> res,List<Integer> list,int curSum,int target,int start){
if(curSum>target){
return;
}
if(curSum==target){
res.add(new ArrayList(list));
return;
}
for(int i=start;i<candidates.length;i++){//i从start开始,只往前不往后
if(i>start&&candidates[i]==candidates[i-1]){
//这一步的剪枝参考https://leetcode-cn.com/problems/combination-sum-ii/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-3/
continue;
}
list.add(candidates[i]);
backtrack(candidates,res,list,curSum+candidates[i],target,i+1);
//从i+1开始因为每个元素只能取一次
list.remove(list.size()-1);
}
}
}
4、216. Combination Sum III
class Solution {
public List<List<Integer>> combinationSum3(int k, int n) {
List<List<Integer>> res=new ArrayList<>();
backtrack(res,new ArrayList<>(),k,n,0,1);
return res;
}
public void backtrack(List<List<Integer>> res,List<Integer> list,int k,int n,int curSum,int start){
if(list.size()==k&&curSum==n){
res.add(new ArrayList<Integer>(list));
return;
}
for(int i=start;i<=9;i++){
if(curSum>n||list.size()>k){
break;
}
list.add(i);
backtrack(res,list,k,n,curSum+i,i+1);
list.remove(list.size()-1);
}
}
}
5、77. Combinations
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
backtrack(res,new ArrayList<>(),n,k,1);
return res;
}
public void backtrack(List<List<Integer>> res,List<Integer> list,int n,int k,int start){
if(list.size()==k){
res.add(new ArrayList(list));//ArrayList传的是引用,所以要复制一个新的ArrayList
return;
}
for(int i=start;i<=n;i++){
list.add(i);
backtrack(res,list,n,k,i+1);
list.remove(list.size()-1);//回溯
}
}
}
6、78. Subsets
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
for(int i=0;i<=nums.length;i++){
backtrack(res,new ArrayList<>(),nums,0,i);
}
return res;
}
public void backtrack(List<List<Integer>> res,List<Integer> list,int[] nums,int start,int size){
if(list.size()==size){
res.add(new ArrayList(list));//ArrayList传的是引用,所以要复制一个新的ArrayList
}
for(int i=start;i<nums.length;i++){
list.add(nums[i]);
backtrack(res,list,nums,i+1,size);
list.remove(list.size()-1);//回溯
}
}
}
7、90. Subsets II
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res=new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i=0;i<=nums.length;i++){
backtrack(res,new ArrayList<>(),nums,0,i);
}
return res;
}
public void backtrack(List<List<Integer>> res,List<Integer> list,int[] nums,int start,int size){
if(list.size()==size){
res.add(new ArrayList(list));//ArrayList传的是引用,所以要复制一个新的ArrayList
}
for(int i=start;i<nums.length;i++){
if(i>start&&nums[i]==nums[i-1]){
continue;//剪枝
}
list.add(nums[i]);
backtrack(res,list,nums,i+1,size);
list.remove(list.size()-1);//回溯
}
}
}
8、131. Palindrome Partitioning
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> res=new ArrayList<>();
backtrack(res,new ArrayList<>(),s,0);
return res;
}
public void backtrack(List<List<String>> res,List<String> list,String s,int start){
if(start==s.length()){
res.add(new ArrayList<>(list));
return;
}
for(int i=start;i<s.length();i++){
if(isPalindrome(s,start,i)){
list.add(s.substring(start,i+1));
backtrack(res,list,s,i+1);
list.remove(list.size()-1);
}
}
}
public boolean isPalindrome(String s,int start,int end){
while(start<end){
if(s.charAt(start)!=s.charAt(end)){
return false;
}
start++;
end--;
}
return true;
}
}