Java中回溯问题的一般方法(子集,排列,回文分区)

1.求子集

给定一组不同的整数,nums,返回所有可能的子集(幂集)。
注意:解决方案集不得包含重复的子集。
例子:
Input: nums = [1,2,3]
Output:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

代码如下:

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> list = new ArrayList();
        Arrays.sort(nums);
        backtrack(list,new ArrayList<>(),nums,0);
        return list;
    }
    private void backtrack(List<List<Integer>>list,List<Integer>tempList,int []nums,int start){
        list.add(new ArrayList<>(tempList));
        for(int i = start;i<nums.length;i++){
            tempList.add(nums[i]);
            backtrack(list,tempList,nums,i+1);
            tempList.remove(tempList.size()-1);
        }
    }
}

给定可能包含重复项,nums的整数集合,返回所有可能的子集(幂集)。

注意:解决方案集不得包含重复的子集。
例子:
Input: [1,2,2]
Output:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]

public List<List<Integer>> subsetsWithDup(int nums){
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list,new ArrayList<>(),nums,0);
    return list;
}
private void backtrack(List<List<Integer>> list,List<Integer> tempList,int []nums,int start){
    list.add(new ArrayList<>(new ArrayList<>(tempList));
    for(int i = start;i<nums.length;i++){
    if(i>start && nums[i]==nums[i-1) continue;//跳过重复的数
    tempList.add(nums[i]);
    backtrack(list,tempList,nums,i+1);
    tempList.remove(tempList.size()-1);
   }
}

3.给定一组不同的整数,返回所有可能的排列。
举例:
Input: [1,2,3]
Output:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

public List<List<Integer>> permute(int [ ] nums){
   List<List<Integer>> list = new ArrayList<>();
   backtrack(list,new ArrayList<>(),nums);
   return list;
}
private void backtrack(List<List<Integer>> list,List<Integer> tempList,int []nums){
if(tempList.size() == nums.length){
  list.add(new ArrayList<>(tempList));
}else{
    for(int i = 0;i<nums.length;i++){
    if(tempList.contains(nums[i])) continue;
    tempList.add(nums[i]);
    backtrack(list,tempList,nums);
    tempList.remove(tempList.size()-1);
 }
}

}

4.给定可能包含重复项的数字集合,返回所有可能的唯一排列。
举例:
Input: [1,1,2]
Output:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

public List<List<Integer>> permuteUnique(int[] nums) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, boolean [] used){
    if(tempList.size() == nums.length){
        list.add(new ArrayList<>(tempList));
    } else{
        for(int i = 0; i < nums.length; i++){
            if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
            used[i] = true; 
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, used);
            used[i] = false; 
            tempList.remove(tempList.size() - 1);
        }
    }
}

给定一组候选数字(候选者)(没有重复)和目标数量(目标),找到候选人数总和目标的候选人中的所有独特组合。

可以从候选者无限次数中选择相同的重复数字。

注意:

所有数字(包括目标)都是正整数。
解决方案集不得包含重复的组合。
举例:Input: candidates = [2,3,6,7], target = 7,
A solution set is:
[
[7],
[2,2,3]
]

Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

public List<List<Integer>> combinationSum(int[] nums, int target) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, target, 0);
    return list;
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
    if(remain < 0) return;
    else if(remain == 0) list.add(new ArrayList<>(tempList));
    else{ 
        for(int i = start; i < nums.length; i++){
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, remain - nums[i], i); // 不是i+1,因为我们可以
            //接受重复的数
            tempList.remove(tempList.size() - 1);
        }
    }
}

给定候选数字(候选者)和目标数量(目标)的集合,找到候选人数量总和为目标的候选人中的所有独特组合。

候选人中的每个号码只能在组合中使用一次。

注意:

所有数字(包括目标)都是正整数。
解决方案集不得包含重复的组合。
举例:
Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

Input: candidates = [2,5,2,1,2], target = 5,
A solution set is:
[
[1,2,2],
[5]
]

public List<List<Integer>> combinationSum2(int[] nums, int target) {
    List<List<Integer>> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, target, 0);
    return list;
    
}

private void backtrack(List<List<Integer>> list, List<Integer> tempList, int [] nums, int remain, int start){
    if(remain < 0) return;
    else if(remain == 0) list.add(new ArrayList<>(tempList));
    else{
        for(int i = start; i < nums.length; i++){
            if(i > start && nums[i] == nums[i-1]) continue; // 跳过重复的数
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, remain - nums[i], i + 1);
            tempList.remove(tempList.size() - 1); 
        }
    }
} 

给定字符串s,分区s使得分区的每个子字符串都是回文。

返回s的所有可能的回文分区。
举例:
Input: “aab”
Output:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]

public List<List<String>> partition(String s) {
   List<List<String>> list = new ArrayList<>();
   backtrack(list, new ArrayList<>(), s, 0);
   return list;
}

public void backtrack(List<List<String>> list, List<String> tempList, String s, int start){
   if(start == s.length())
      list.add(new ArrayList<>(tempList));
   else{
      for(int i = start; i < s.length(); i++){
         if(isPalindrome(s, start, i)){
            tempList.add(s.substring(start, i + 1));
            backtrack(list, tempList, s, i + 1);
            tempList.remove(tempList.size() - 1);
         }
      }
   }
}

public boolean isPalindrome(String s, int low, int high){
   while(low < high)
      if(s.charAt(low++) != s.charAt(high--)) return false;
   return true;
} 

你可能感兴趣的:(算法学习)