Day29~39. 组合总和、40.组合总和II、131.分割回文串

64 阅读4分钟

摘要

本文主要介绍了LeetCode回溯算法的几个题目,包括39. 组合总和、40.组合总和II、131.分割回文串。

1、39.组合总和

1.1 思路

40.组合总和II

  • 首先检查是否达到了目标值 target,如果达到目标值,就将当前路径 path 加入结果列表 list 中,并返回。
  • 然后,使用一个循环遍历候选数组 candidates,从 start 位置开始。对于每个候选数 candidates[i],它检查是否加入这个候选数会导致目标值 target 超过,如果超过了,就跳过这个候选数,继续下一个。
  • 如果没有超过目标值,就将这个候选数加入当前路径 path 中,然后递归调用 doCombinationSum 方法,继续寻找下一个候选数,但是起始位置从 i 开始,因为可以重复使用相同的数字
  • 在递归返回后,需要撤销对当前候选数的选择,即从路径 path 中移除,以便尝试其他的组合方式。

1.2 代码

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList<>();
        LinkedList path = new LinkedList<>();
        doCombinationSum(list, path, candidates, target, 0);
        return list;
    }
​
    public void doCombinationSum(List<List<Integer>> list, LinkedList path, int[] candidates, int target, int start) {
        if(target == 0) {
            list.add(new ArrayList<>(path));
            return;
        }
        
        for(int i=start; i<candidates.length; i++) {
            if(target-candidates[i] < 0) {
                continue;
            }
​
            path.add(candidates[i]);
​
            doCombinationSum(list, path, candidates, target-candidates[i], i);
​
            path.removeLast();
        }
    }

2、40.组合总和II

2.1 思路

40.组合总和II

  • 初始化了结果列表 list 和路径 path并将候选数组 candidates 进行排序,以确保相同的数字都放在一起。
  • 首先检查是否达到了目标值 target,如果达到目标值,就将当前路径 path 加入结果列表 list 中,并返回。
  • 接下来,使用一个循环遍历候选数组 candidates,从 start 位置开始。对于每个候选数 candidates[i],它检查是否加入这个候选数会导致目标值 target 超过,如果超过了,就跳出循环,因为数组已经排序,后面的数字都会更大。
  • 如果没有超过目标值,就将这个候选数加入当前路径 path 中,然后递归调用 doCombinationSum2 方法,继续寻找下一个候选数,但是起始位置从 i+1 开始,因为不能重复使用相同的数字。
  • 在递归返回后,需要撤销对当前候选数的选择,即从路径 path 中移除,以便尝试其他的组合方式。
  • 注意这里的剪枝操作,通过 if (i > start && candidates[i] == candidates[i - 1]) 来跳过相同层级使用过的相同数字,以避免重复解的产生。

2.2 代码

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> list = new ArrayList<>();
        LinkedList path = new LinkedList<>();
        //为了将重复的数字都放到一起,所以先进行排序
        Arrays.sort(candidates);
        doCombinationSum2(list, path, candidates, target, 0);
        return list;
    }
​
    public void doCombinationSum2(List<List<Integer>> list, LinkedList path, int[] candidates, int target, int start) {
        if(target == 0) {
            list.add(new ArrayList<>(path));
            return;
        }
        
        for(int i=start; i<candidates.length; i++) {
            if(target-candidates[i] < 0) {
                break;
            }
            //正确剔除重复解的办法
            //跳过同一树层使用过的元素
            if ( i > start && candidates[i]==candidates[i - 1] ) {
                continue;
            }
​
            path.add(candidates[i]);
​
            doCombinationSum2(list, path, candidates, target-candidates[i], i+1);
​
            path.removeLast();
        }
    }

3、131.分割回文串

3.1 思路

131.分割回文串

  • 首先检查是否已经遍历完了字符串 s,如果是的话,就将当前路径 path 加入结果列表 list 中,并返回。
  • 接下来,使用一个循环遍历字符串 s,从 start 位置开始。对于每个 i,它调用 isPalindrome 方法来检查从 starti 的子串是否是回文串。如果是回文串,就将这个子串加入当前路径 path 中,然后递归调用 doPartition 方法,继续寻找下一个回文子串。
  • 在递归返回后,需要撤销对当前回文子串的选择,即从路径 path 中移除,以便尝试其他的回文子串。
  • isPalindrome 方法用于判断一个字符串是否是回文串,它使用双指针的方式,从字符串的两端开始比较字符,如果字符不相等,就返回 false,否则继续比较直到两指针相遇。

3.2 代码

    // 回溯+判断是否是回文串
    public List<List<String>> partition(String s) {
        List<List<String>> list = new ArrayList<>();
        LinkedList<String> path = new LinkedList<>();
        doPartition(list, path, s, 0);
        return list;
    }
​
    public void doPartition(List<List<String>> list, LinkedList<String> path, String s, int start) {
        if(start >= s.length()) {
            list.add(new ArrayList<>(path));
            return;
        }
​
        for(int i=start; i<s.length(); i++) {
            if(!isPalindrome(s, start, i)) {
                continue;
            }
​
            path.add(s.substring(start, i + 1));
​
            doPartition(list, path, s, i + 1);
​
            path.removeLast();
        }
    }
​
    // 是否是回文串
    private boolean isPalindrome(String s, int left, int right) {
        int start = left;
        int end = right;
        while(start < end) {
            if(s.charAt(start) != s.charAt(end)) {
                return false;
            }
            start++;
            end--;
        }
        return true;
    }

参考资料

代码随想录-39. 组合总和

代码随想录-40.组合总和II

代码随想录-131.分割回文串