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

39. 组合总和

这道题和前面组合问题的区别是,取的元素可以重复,也就是遍历的时候,同一个元素可以一直取。所以for循环里,逐个添加元素,判断和大于目标时break(否则会一直加)

还是新建二维数组放结果,一维数组放path。输入参数为放结果数组、path、提供的数组、目标值、目前总和sum、startIndex

提前把提供的数组排序,用Arrays.sort() 这样sum超过target就break

递归的时候,每次startIndex都从i开始,不从i+1,这样同一个数字就可以重复

最后回溯的时候用path.removeLast()就行,不需要答案上那种具体的path.remove(path.size() - 1);

40.组合总和II

和组合总和那道题的区别,本题的难点在于中:集合(数组candidates)有重复元素,但还不能有重复的组合。

这就要去重。包括树枝去重和树层去重。用哪个?用树层去重。

去重逻辑 整理思路看视频:[https://www.bilibili.com/video/BV12V4y1V73A/?vd_source=07371fb33253dfd67e5965271ead821b](https://)
3分50秒--11分50秒部分

注意点:

为了能用树层去重,判断candidates[i] == candidates[i - 1],提前要将重复的数字都放到一起,所以先进行排序

新建布尔数组used,标记提供的数组某一位数字有没有被用过

candidates[i] == candidates[i - 1]是判断同层或同树一样
candidates[i] == candidates[i - 1] && !used[i - 1]是判断同层有一样,因为同树一样的话used[i - 1]就是true,因为用过

知识点:

新建长度为...的布尔形式数组 boolean[] used = new boolean[candidates.length];

131.分割回文串 

其实切割问题类似组合问题。
例如对于字符串abcdef:
组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个.....。
切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段.....。

在代码里什么是切割线呢?递归参数需要传入startIndex,表示下一轮递归遍历的起始位置,这个startIndex就是切割线。

在递归循环中如何截取子串呢?在for (int i = startIndex; i < s.size(); i++)循环中,我们 定义了起始位置startIndex,那么 [startIndex, i] 就是要截取的子串。判断这个子串是不是回文,如果是回文,就加入

注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。

注意切割过的位置,不能重复切割,所以,backtracking(s, i + 1); 传入下一层的起始位置为i + 1。

用双指针法判断是否是回文

错误的地方1:

        StringBuilder sb = new StringBuilder();
        for(int i = startIndex; i < s.length(); i++){
            // StringBuilder sb = new StringBuilder();
            sb.append(s.charAt(i));
            if(check(sb)){
                path.add(sb.toString());
                backTracing(s, i+1);
                path.removeLast();
            }
        }

StringBuilder sb = new StringBuilder();应该写在for循环外面。因为执行for循环时,每次递加一次,里面就要重新执行一次。如果新建sb放在里面,每次都只拼接一个字母,就会重新初始化。放在外面才能一直拼

错误的地方2:
if(check(sb)){
   ......
}
 public boolean check(StringBuilder sb){
       for(int i = 0; i < sb.length()/2; i++){
          if(sb.charAt(i) != sb.charAt(sb.length() - i -1)){
              return false;
          }
       }
       return true;
}

这样结果是对的

if(check(sb)){
   ......
}

 public boolean check(StringBuilder sb){
       for(int i = 0; i < sb.length()/2; i++){
          if(sb.charAt(i) == sb.charAt(sb.length() - i -1)){
              return true;
          }
       }
       return false;
}

这样结果是错的

想一下这两个区别是什么?
第一段是一一检查,发现不对应的就返回false
第二段是一一检查,发现对应的就返回true。也就是第二段只要有一个对的就判断为true
但是题目要判断回文,必须全部对才行

你可能感兴趣的:(刷题,算法,数据结构)