代码随想录算法训练营第二十七天|39. 组合总和、40.组合总和II、131.分割回文串

代码随想录算法训练营第二十七天|39. 组合总和、40.组合总和II、131.分割回文串

39. 组合总和

题目链接

问题简述:在无重复元素的candidates中找出所有和为target的组合,candidates中元素可重复使用。

思考:感觉好几道组合总和都很相似,但都有细节差别。

算法思路

  • 定义一个startIndex作为每次循环的起始位置,path为每个结果的路径,sum为当前路径的总和。进
  • 行操作时首先判断当前的sum是否大于target,如果大于target直接返回并回溯,然后判断当path元素总和为target时,将path加入lists并返回。
  • 如果都不满足,依次从startIndex开始遍历所有元素,每次遍历在path中加入startIndex位置元素,sum中加入当前元素,再进行递归,递归时保证第一次递归仍需要加入当前元素作为起始位置,即startIndex + index位置的元素,index初始值为0,每次循环依次加一,保证path中元素可以重复。递归完成后再移除上一步加入的元素,并将sum剪去当前元素值。
import java.util.ArrayList;
import java.util.List;

class Solution {
    List<List<Integer>> lists = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int sum = 0;
  
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        backtracking(candidates, target, 0);
        return lists;
    }

    public void backtracking(int[] candidates, int target,int startIndex){
        //进行剪枝
        if (sum > target) return;
        //确定结束条件
        if (sum == target) {
            lists.add(new ArrayList<>(path));
            return;
        }
        //用来确定下一次循环的其实位置
        int index = 0;
        for (int i = startIndex; i < candidates.length; i++) {
            path.add(candidates[i]);
            sum += candidates[i];
            //将当前元素添加后,下一次仍可以添加这个元素,但再次循环就不需要再添加当前元素了
            backtracking(candidates, target, startIndex + index);
            index++;
            //回溯
            sum -= candidates[i];
            path.remove(path.size() - 1);
        }
    }
}

40.组合总和II

题目链接

问题简述:在有重复元素的candidates中找出所有和为target的组合,candidates中的每个数字最多使用一次,结果集中不允许重复,但组合中的元素可以因candidates有重复元素而重复。

思考:这个问题的关键在于去重,需要注意的细节非常非常多,感觉非常难了。

算法思路

  • 定义一个startIndex作为每次递归中candidates的起始位置;path存储每个结果集;lists存储path的集合;sum为当前path元素和;定义一个used数组,长度为candidates长度,初始元素为0,used用来标记当前元素是否在path中,如果在的话变为1,进行回溯时变回0。
  • 先对candidates进行排序,排序后有重复的元素。
  • 递归函数的结束条件为sum等于target,这时将path加入lists;同时进行剪枝,如果sum大于target,则之前返回然后回溯。
  • 每次递归从startIndex指向的元素加入path,先进行去重,判断当前元素是否等于上一个元素,如果等于的话,还要判断used[i - 1]是否等于0,如果不为0说明上一个元素正在当前的path路径中,而path中可以包含重复的元素,所以不去重;只有当used[i - 1] == 0时,我们已经确定包含上个元素的所有组合已经加入lists,才可以进行去重。然后遍历所有元素,每次加入当前元素,更改sum,更改used标志位,然后进行递归下一个位置元素,最后进行回溯,更改used、sum并推出path元素。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

class Solution {
    List<List<Integer>> lists = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    int sum = 0;

    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
      	//used用来标记当前元素是否在path中,如果在的话变为1,进行回溯时变回0。
        int[] used = new int[candidates.length];
        Arrays.sort(candidates);
        backtracking(candidates, target, 0, used);
        return lists;
    }

    public void backtracking(int[] candidates, int target,int startIndex, int[] used){
        //进行剪枝
        if (sum > target) return;
        //确定结束条件
        if (sum == target) {
            lists.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i < candidates.length; i++) {
          	//进行去重,判断当前元素是否等于上一个元素,如果等于的话,还要判断used[i - 1]是否等于0,如果不为0说明上一个元素正在当前的path路径中,而path中可以包含重复的元素,所以不去重;只有当used[i - 1] == 0时,我们已经确定包含上个元素的所有组合已经加入lists,才可以进行去重
            if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == 0) continue;
            path.add(candidates[i]);
            sum += candidates[i];
            used[i] = 1;
            //递归下一个元素
            backtracking(candidates, target, i + 1, used);
            //回溯
            used[i] = 0;
            sum -= candidates[i];
            path.remove(path.size() - 1);
        }
    }
}

131.分割回文串

题目链接

问题简述:找到将字符串分割为回文串的所有可能。

思考:开始想着吧s中字符都存入新的数组中,后来发现没必要,主要是对字符串的函数不熟悉,比如函数的范围是左闭右开,错误找了半天才懂。

算法思路

  • 定义函数用双指针判断字符串的[left,right]范围是否为回文串。

  • 定义lists存储所有组合,定义path存储当前分割的组合。startidx为当前指向的字符。

  • 递归函数中如果当前的startidx出界则将path加入lists并返回,如果未出界则依次从startIdx遍历所有元素,每次判断startIndex到当前i是否为回文串,如果是回文串则将这一段加入path,然后继续递归这一段之后的字符串后,进行回溯,即将path最近的回文串移除。

import java.util.ArrayList;
import java.util.List;

class Solution {
    //定义返回值
    List<List<String>> lists = new ArrayList<>();
    List<String> path = new ArrayList<>();

    public List<List<String>> partition(String s) {
        backtracking(s, 0);
        return lists;
    }
    //回溯函数
    public void backtracking(String s, int startidx){
        //如果当前的startidx出界则将path加入lists
        if (startidx >= s.length()) {
            lists.add(new ArrayList<>(path));
            return;
        }
        //依次遍历每个元素,判断startidx到当前i元素是否为回文串
        for (int i = startidx; i < s.length(); i++) {
            if (isDestring(s, startidx, i)){
                //如果是回文串则将串加入进path,并递归再回溯,substring方法是加入左闭右开的字符
                String str = s.substring(startidx, i + 1);
                path.add(new String(str));
                backtracking(s, i + 1);
                //回溯
                path.remove(path.size() - 1);
            }
        }
    }
    //判断是否为回文串,取left到right的左闭右闭区间
    public boolean isDestring(String s, int left, int right){
        while (left <= right){
            if (s.charAt(left) != s.charAt(right)) return false;
            left++;
            right--;
        }
        return true;
    }
}

感想

创立柱间拿到了,感觉过程一直很期待,到手了也就是那回事吧。然后还买到了rex还有吉迪恩的套装,真的超级开心,明天争取完成一天算法的同时,再学习一下项目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值