代码随想录-2023/07/24

58 阅读1分钟

回溯算法

39.组合总和

  1. 注意每个字符可以无限制被取, 所以需要每次递归到下一层的时候传入的下标索引为当前位置的索引i
  2. 需要排序, 实现当大于target的时候就退出递归

代码:

class Solution {
    List<List<Integer>> ans = new ArrayList<>();
    LinkedList<Integer> list = new LinkedList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        // 排序---确保当大于target的时候可以返回, 后面不存在解
        Arrays.sort(candidates);
        backtracking(candidates, target, 0, 0);
        return ans;
    }

    public void backtracking(int[] candidates, int target, int start, int sum){
        if(sum > target) return;
        if(sum == target) {
            ans.add(new ArrayList<>(list));
            return;
        }
        for(int i=start; i<candidates.length; i++){
            list.add(candidates[i]);
            backtracking(candidates, target, i, sum+candidates[i]);
            list.removeLast();
        }
    }
}

40.组合总和2

  1. 本题要求每个数字只能使用一次, 所以传入到下一层递归的索引start 为 i+1
  2. 同时需要排序将相同的元素放到一起, 然后进行横向去重

代码:

class Solution {
    // 去重问题---考虑每层从0开始, 从i开始, 从i+1开始
    // 从0开始: 不去重, 数字顺序不同也视为一种组合
    // 从i开始: 每个元素可以使用无数次, 但是实现了顺序不同的去重
    // 从i+1开始: 每个元素只能使用一次
    List<List<Integer>> ans = new ArrayList<>();
    LinkedList<Integer> list = new LinkedList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backtracking(candidates, target, 0, 0);
        return ans;
    }
    public void backtracking(int[] candidates, int target, int start, int sum){
        if(sum > target) return;
        if(sum == target){
            ans.add(new ArrayList<>(list));
            return;
        }
        // 每层一个哈希表进行横向去重
        HashSet<Integer> set = new HashSet<>();
        for(int i=start; i<candidates.length; i++){
            if(set.contains(candidates[i])) continue;
            // 等价于
            if(i > start && candidates[i] == candidates[i-1]) continue;
            set.add(candidates[i]);
            list.add(candidates[i]);
            backtracking(candidates, target, i+1, sum+candidates[i]);
            list.removeLast();
        }
    }


}

131.分割回文子串

  1. 每层分割的起点视为start, 然后从start开始遍历终点, 对于每个区间内的子串进行判断
  2. 若为回文串, 则视为有效分割, 继续开启下一层递归分割
  3. 若不是回文串, 则代表当前分割失败, 继续往后循环, 逐一找到当前分割成功的地方

代码:

class Solution {
    List<List<String>> ans = new ArrayList<>();
    LinkedList<String> path = new LinkedList<>();
    public List<List<String>> partition(String s) {
        backtranking(s, 0);
        return ans;
    }

    public void backtranking(String s, int start){
        if(start == s.length()){
            ans.add(new ArrayList<>(path));
            return;
        }
        // 每层的起点是index, 终点是i+1
        for(int i=start; i<s.length(); i++){
            String str = s.substring(start, i+1);
            if(checkPalindrome(str)){
                path.add(str);
                backtranking(s, i+1);
                path.removeLast();
            }
        }
    }

    public boolean checkPalindrome(String str){
        return str.equals(new StringBuffer(str).reverse().toString());
    }
}