随想录训练营Day27 | 回溯 39.组合总和, 40.组合总和II, 131.分割回文串

90 阅读3分钟

随想录训练营Day27 | 回溯 39.组合总和, 40.组合总和II, 131.分割回文串

标签: LeetCode闯关记


39. 组合总和

  • 本题和我们之前讲过的77.组合、216.组合总和III 有两点不同:
    • 组合没有数量要求
    • 元素可无限重复选取

个人思路: 把216.组合总和III的代码稍稍改改就好

  • 个人AC代码
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    int sum = 0;

    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        find(candidates, target, 0);
        return res;
    }
    public void find(int[] candidates, int target, int startIndex){
        if(sum > target){
            return;
        }
         if(sum == target){
             res.add(new ArrayList<>(path));
             return;
            }


        for (int i = startIndex; i < candidates.length ; i++) {
            path.add(candidates[i]);
            sum += candidates[i];
            find(candidates,target,i);
            sum -= candidates[i];
            path.removeLast();
        }
    }
}

优化(没有想到):

  • 对总集合排序之后,如果下一层的sum(就是本层的 sum + candidates[i])已经大于target,就可以结束本轮for循环的遍历。
  • 先排序 : Arrays.sort(candidates); // 先进行排序
  • 再判断(如下)
for (int i = startIndex; i < candidates.length; i++) {
            // 如果 sum + candidates[i] > target 就终止遍历
            if (sum + candidates[i] > target) break;
            path.add(candidates[i]);
            find(res, path, candidates, target, sum, i);
            path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素
            sum -= candidates[i];
        }

40.组合总和II

和39.组合总和 (opens new window)如下区别:

  • 本题candidates仅能使用一次。
  • 数组candidates的元素是有重复 难点: 去重
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);
        backTracing(candidates,target,0,used);
        return res;

    }
    public void backTracing(int[] candidates, int target,int startIndex, boolean[] used){
        if(sum == target){
            res.add(new ArrayList<Integer>(path));
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
            
            //借助used数组去重,以此区分树层重复or树枝重复,牛逼! key
            if(i >= 1 && candidates[i] == candidates[i-1] && used[i -1] == false){
                    continue;//犯错,写成了break;
            }
            //39. 组合总和学到的剪枝
            if(sum + candidates[i] > target){
                break;
            }
            sum += candidates[i];
            path.add(candidates[i]);
            used[i] = true;
            backTracing(candidates,target, i + 1,used);
            sum -= candidates[i];
            used[i] = false;
            path.removeLast();

        }
    }
}

实现中出现问题:

  • 去重操作:只写了"if(i >= 1 && candidates[i] == candidates[i-1])",导致把所有正确结果去掉了,output为空, 没有进行数层去重;
  • 把两个if判断写在一起,并写成了"break",错因,没有正确区分,①剪枝的时候,一旦发现"sum + candidates[i] > target",可以退出循环,即break;②发现数层去重的时候,应跳过当前元素,往后继续循环;
    • chatGPT解释
      • 使用 continue 语句可以跳过当前循环中的迭代,继而进行下一轮循环。而使用 break 语句会直接结束当前循环,不再执行后续的迭代。
      • 如果将 continue 改为 break,则可能会导致错误的结果。因为在这个算法中,当发现一个数与前一个数相等且前一个数没有使用时,需要跳过当前的循环,但不需要结束循环。如果使用 break 来代替 continue,则会直接结束当前循环,无法完成后续的判断。

用时: 1.2h

131.分割回文串

难点: 找分割的分界线(完全没有想到 starIndex可以来当作终止条件的判断) 这里有点意思的是分割, 也就是把子串放在一个结果集里面.

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

    }
    private void findPartition(String s, int startIndex){
        if(startIndex == s.length()){
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //判断回文串
            if(isPalindrome(s,startIndex,i)){
                String substr = s.substring(startIndex, i + 1);
                path.add(substr);
                findPartition(s,i + 1);//,在找到一个回文子串后,将其加入到 path 中,并直接调用 findPartition() 方法,然后再从 path 中移除最后一个元素。这种实现方式避免了重复操作路径 path 的开销,可以提高算法的效率。
                path.removeLast();
            }else{
                continue;
            }
           


        }
    }
    private boolean isPalindrome(String s, int start, int end){
    
        while(start <= end){
            if(s.charAt(start) != s.charAt(end)){
                return false;
            }
            start++;
            end--;
        }
        return true;
    }
}

出现问题: 在判断回文串时,双指针终止条件写错, while(start >= end);(无语) 难点: 理解如何分割,分割方式和终止条件