代码随想录day27 || 回溯算法 || 组合总和 || 组合总和Ⅱ || 分割回文串

39. 组合总和

● 力扣题目链接
● 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
● candidates 中的数字可以无限制重复被选取。

思路

● 数字可以重复选,因此我们每次递归从i开始,而不是i + 1
● 考虑剪枝,我们可以先把数组排序,然后一旦某时大于了tar,就直接break
● 时间复杂度:O(n * 2^n)

代码

class Solution {
    List<List<Integer>> res = new ArrayList();
    LinkedList<Integer> path = new LinkedList();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates); // 先排序,方便后续剪枝
        candidatesHelper(candidates, target, 0, 0);
        return res;
    }
    private void candidatesHelper(int[] candidates, int target, int sum, int startIndex) {
        if (sum >= target) {
            if (sum == target) {
                res.add(new ArrayList<>(path));
            }
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
            if (sum + candidates[i] > target) break; // 如果这个元素加上已经大于tar了,就不用再递归了
            path.addFirst(candidates[i]);
            sum += candidates[i];
            candidatesHelper(candidates, target, sum, i);
            sum -= candidates[i];
            path.removeFirst();
        }
    }
}

40.组合总和II

● 力扣题目链接
● 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
● candidates 中的每个数字在每个组合中只能使用一次。
● 说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。

思路

● 难点在于,数组中有重复的元素,还不能有重复的组合
● 我们要去重的是同一树层使用过的,同一树枝使用过的不用去重
○ 可以使用startIndex解决,遍历本层(循环)的时候,看一下不是第一个,且和前一个元素相同,那就结束本次循环
○ 或者我们使用used数组进行标记
■ 如果是同层的,递归回来会把used对应位置改回false
■ 如果是同枝的,不会改为false
● 时间复杂度:O(n * 2^n)

代码

class Solution {
    List<List<Integer>> res = new ArrayList();
    LinkedList<Integer> path = new LinkedList();
    int sum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        combinationSum2Helper(candidates, target, 0);
        return res;
    }
    private void combinationSum2Helper(int[] candidates, int target, int startIndex) {
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
            // 去重同一树层使用过的元素
            if (i > startIndex && candidates[i] == candidates[i - 1]) continue;
            if (candidates[i] + sum > target) break;
            sum += candidates[i];
            path.addFirst(candidates[i]);
            combinationSum2Helper(candidates, target, i + 1);
            path.removeFirst();
            sum -= candidates[i];
        }
    }
}

class Solution {
    List<List<Integer>> res = new ArrayList();
    LinkedList<Integer> path = new LinkedList();
    int sum = 0;
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        boolean[] used = new boolean[candidates.length];
        Arrays.fill(used, false);
        combinationSum2Helper(candidates, target, used, 0);
        return res;
    }
    private void combinationSum2Helper(int[] candidates, int target, boolean[] used, int startIndex) {
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
            if (i > startIndex && candidates[i] == candidates[i - 1] && !used[i]) continue;
            if (sum + candidates[i] > target) break;
            sum += candidates[i];
            path.addFirst(candidates[i]);
            used[i] = true;
            combinationSum2Helper(candidates, target, used, i + 1);
            used[i] = false;
            path.removeFirst();
            sum -= candidates[i];
        }
    }
}

131.分割回文串

● 力扣题目链接
● 给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
● 返回 s 所有可能的分割方案。
● 示例: 输入: “aab” 输出: [ [“aa”,“b”], [“a”,“a”,“b”] ]

思路

● startIndex是切割线
● 递归终止条件:切割线到了字符串末尾
● 单层递归逻辑:遍历字符串,一旦发现回文串,就加入path,然后回溯,继续向后看有没有回文串,回溯结束之后从path中弹出

代码

class Solution {
    List<List<String>> res = new ArrayList();
    Deque<String> path = new ArrayDeque();
    public List<List<String>> partition(String s) {
        partitionHelper(s, 0);
        return res;
    }
    private void partitionHelper(String s, int startIndex) {
        // 遍历到末尾,找到了符合要求的,加入list
        if (startIndex == s.length()) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            if (isPalind(s, startIndex, i)) { // 如果这一段是回文串
                path.addLast(s.substring(startIndex, i + 1)); // 把这一段加入path
                partitionHelper(s, i + 1); // 继续递归
                path.removeLast(); // 要回溯弹出,找下一段
            }
        }
    }
    // 判断字符串s在start到end这一段是不是回文串
    private boolean isPalind(String s, int start, int end) {
        while (start < end) {
            if (s.charAt(start) != s.charAt(end)) return false;
            start++;
            end--;
        }
        return true;
    }
}

你可能感兴趣的:(代码随想录,算法,数据结构,leetcode,java)