代码随想录Day29

39.组合总和

题目:39. 组合总和 - 力扣(LeetCode)

思路:可以重复选取,这个要怎么回溯?而且是无限制的重复。每次回溯,都还是遍历当前的数组,记住数组中的最小值,一旦发现path的值加上最小值大于目标值,直接return,

尝试
class Solution {
    List<List<Integer>> result= new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int min = Integer.MAX_VALUE;
    int sum = 0;
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        for(int i : candidates){
            if(i < min) min = i;
        }
        backTracking(candidates,target,sum);
        return result;
    }
    public void backTracking(int[] candidates, int target,int sum){
        if(sum == target ){
           // result.add(path);
            result.add(new ArrayList<>(path));
            return;
        }
        if(sum > target){
            return;
        }
        for(int i = 0;i < candidates.length;i++){
            sum += candidates[i];
            path.add(candidates[i]);
            backTracking(candidates,target,sum);
            path.remove(path.size()-1);
            sum -=candidates[i];  
        }
    }
}

🍉返回结果时,要用【 result.add(new ArrayList<>(path));】 

🍉我尝试中出现的问题是,会有【2,2,3】【2,3,2】这样的重复组合

答案
// 剪枝优化
class Solution {
    List<List<Integer>> res = new ArrayList<>();
    List<Integer> path = new  ArrayList<>();
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates); // 先进行排序
        backtracking(candidates, target, 0, 0);
        return res;
    }

    public void backtracking(int[] candidates, int target, int sum, int idx) {
        // 找到了数字和为 target 的组合
        if (sum == target) {
            res.add(new ArrayList<>(path));
            return;
        }

        for (int i = idx; i < candidates.length; i++) {
            // 如果 sum + candidates[i] > target 就终止遍历
            if (sum + candidates[i] > target) break;
            path.add(candidates[i]);
            backtracking(candidates, target, sum + candidates[i], i);
            path.remove(path.size() - 1); // 回溯,移除路径 path 最后一个元素
        }
    }
}
复习

离谱,就是错了一个参数!传入的应该是【i】,而不是startIndex,传【startIndex】就定死了

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>(); 
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        backTracking(candidates,target,0,0);
        return result;
    }
    public void backTracking(int[] candidates,int target,int sum,int startIndex){
        if(sum == target){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i = startIndex; i < candidates.length;i++){
            if(sum + candidates[i] > target) break;
            path.add(candidates[i]);
            backTracking(candidates,target,sum + candidates[i],startIndex);
            path.remove(path.size() - 1);
        }
    }
}
小结

🍉对于组合问题,如果是一个集合来求组合的话,就需要startIndex,多个集合取组合,各个集合之间相互不影响,那么就不用startIndex

🍉终止遍历,用break

if (sum + candidates[i] > target) break;

🍉递归函数的参数【idx】,作用就是防止组合重复,如下图,取【2】之后,可以在【2,3,5】中接着取,但是取【5】之后只能在【5,3】中接着取

40.组合总和||

题目:40. 组合总和 II - 力扣(LeetCode)

思路:跟组合总和应该是类似的,只能使用一次,注意candidate里面有重复元素,搞一个指针,不断移动地去加,这样就能保证一个元素只被用了一次,怎么实现在同一个path下移动指针,感觉现在需要两个指针

尝试
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backTracking(candidates,target,0,0);
        return result;
    }
    public void backTracking(int[] candidates, int target,int sum,int idx){
        if(sum == target){
            result.add(new ArrayList<>(path));
        }
        for(int i = idx; i < candidates.length; i++){
            if(sum +candidates[i] > target) break;
            path.add(candidates[i]);
            backTracking(candidates,target,sum + candidates[i],i+1);
            path.remove(path.size() - 1);
        }
    }
}

出现了重复组合,关键还是不知道怎么移动指针 

 修改(我靠,加一个if函数就AC了)
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backTracking(candidates,target,0,0);
        return result;
    }
    public void backTracking(int[] candidates, int target,int sum,int idx){
        if(sum == target){
            result.add(new ArrayList<>(path));
        }
        for(int i = idx; i < candidates.length; i++){
            if(sum +candidates[i] > target) break;
            if (i > idx && candidates[i] == candidates[i - 1]) {
                continue;
            }
            path.add(candidates[i]);
            backTracking(candidates,target,sum + candidates[i],i+1);
            path.remove(path.size() - 1);
        }
    }
}
复习
class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        Arrays.sort(candidates);
        backTracking(candidates,target,0,0);
        return result;
    }
    public void backTracking(int[] candidates, int target,int sum,int startIndex){
        if(sum == target){
            result.add(new ArrayList<>(path));
            return;
        }
        for(int i = startIndex; i < candidates.length ;i++){
            if(i>startIndex && candidates[i] == candidates[i-1]) break;
            if(sum + candidates[i] > target) return;
            path.add(candidates[i]);
            backTracking(candidates,target,sum+candidates[i],i+1);
            path.remove(path.size() - 1);
        }
    }
}
答案
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 );
    backTracking( candidates, target, 0 );
    return res;
  }
  
  private void backTracking( int[] candidates, int target, int start ) {
    if ( sum == target ) {
      res.add( new ArrayList<>( path ) );
      return;
    }
    for ( int i = start; i < candidates.length && sum + candidates[i] <= target; i++ ) {
      //正确剔除重复解的办法
      //跳过同一树层使用过的元素
      if ( i > start && candidates[i] == candidates[i - 1] ) {
        continue;
      }

      sum += candidates[i];
      path.add( candidates[i] );
      // i+1 代表当前组内元素只选取一次
      backTracking( candidates, target, i + 1 );

      int temp = path.getLast();
      sum -= temp;
      path.removeLast();
    }
  }
}
小结

🍉现在明白了,【for循环】是横向遍历,【递归】是纵向遍历

 🍉【for循环】时,每个数字是不能重复的,比如【1,1,2,3】,target为【3】,树层不去重,就会出现两个【1,2】

 🍉如果总和大于目标值,应该是【break】,for循环跳过集合的重复数字,应该用【continue】

131.分割回文串

题目:131. 分割回文串 - 力扣(LeetCode)

思路:印象中回文串要用双指针,为啥感觉遍历就行,回溯干啥啊,难在回溯时,还要判断截取的内容是否回文

复习

答案
class Solution {
    List<List<String>> lists = new ArrayList<>();
    Deque<String> deque = new LinkedList<>();

    public List<List<String>> partition(String s) {
        backTracking(s, 0);
        return lists;
    }

    private void backTracking(String s, int startIndex) {
        //如果起始位置大于s的大小,说明找到了一组分割方案
        if (startIndex >= s.length()) {
            lists.add(new ArrayList(deque));
            return;
        }
        for (int i = startIndex; i < s.length(); i++) {
            //如果是回文子串,则记录
            if (isPalindrome(s, startIndex, i)) {
                String str = s.substring(startIndex, i + 1);
                deque.addLast(str);
            } else {
                continue;
            }
            //起始位置后移,保证不重复
            backTracking(s, i + 1);
            deque.removeLast();
        }
    }
    //判断是否是回文串
    private boolean isPalindrome(String s, int startIndex, int end) {
        for (int i = startIndex, j = end; i < j; i++, j--) {
            if (s.charAt(i) != s.charAt(j)) {
                return false;
            }
        }
        return true;
    }
}
小结

 🍉因为对于一个集合,存在多种分割方案,所以要用回溯

🍉【subString】要传入的是(起始位置指针),以及(结尾指针+1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值