Day21 力扣回溯 : 216.组合总和III | 17.电话号码的字母组合

Day21 力扣回溯 : 216.组合总和III | 17.电话号码的字母组合

216.组合总和III

如果把 组合问题理解了,本题就容易一些了。

题目链接/文章讲解:https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html
视频讲解:https://www.bilibili.com/video/BV1wg411873x

第一印象:

感觉是可以套模板,但我对剪枝的逻辑一点思路没有,直接看题解学习一下模板的使用。

看完题解的思路:

我觉得思路不难,还是挺模板题的,下次做题的时候得学着自己画那个树形图了。

代码随想录里的图:

在这里插入图片描述

这道题不像第一个组合,只需要path添添删删,因为只是需要路径长度而已。而这道题不仅需要路径长度,还需要这些数字的总和sum满足target。

所以for循环做操作的时候要对path和sum一起操作,并回溯。

返回值和参数:

回溯大部分返回值void,参数除了题里的n、k和startIndex外,因为要记录path内的总和,所以也要有个sum。这样在递归给子节点的时候就有一种:现在已经有这么多了。的感觉了

终止条件:

k限制树的高度,因为只需要k个数字,再往下就没有意义了。所以当path里的数字有k个了,就可以终止了。这道题还需要判断sum是否和target相等,满足要求的才需要加入result.

单层递归逻辑:

这道题不像第一个组合,只需要path添添删删,因为只是需要路径长度而已。而这道题不仅需要路径长度,还需要这些数字的总和sum满足target。

所以for循环做操作的时候要对path和sum一起操作,并回溯。

现在去学习一下tmd剪枝吧。

这道题的剪枝其实是我不敢去想哈哈哈,看了一下觉得简单的。

就是如果sum已经超过target了,就没必要继续了。

所以在递归函数的开头可以写

if (sum > target) return;

其次是for循环里的因为k(个数)限制的剪枝,和上一道题是一样的:

for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {

实现中的困难:

第一次做的时候把for循环的终止条件想当然弄成path.size()了

感悟:

剪枝有点自由发挥,但是对于for循环里的因为k(个数)限制的剪枝有了更深的理解,虽然这种 +1 真的恶心人。

代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();

    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(k, n, 0, 1);
        return result;

    }

    private void backtracking(int k, int target, int sum, int startIndex) {
        if (sum > target) return;

        if (path.size() == k) {
            if (sum == target) {
                result.add(new ArrayList<>(path));
            }
            return;
        }

        for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
            path.add(i);
            sum += i;
            backtracking(k, target, sum, i + 1);
            path.removeLast();
            sum -= i;
        }
    }
}

17.电话号码的字母组合

本题大家刚开始做会有点难度,先自己思考20min,没思路就直接看题解。

题目链接/文章讲解:https://programmercarl.com/0017.%E7%94%B5%E8%AF%9D%E5%8F%B7%E7%A0%81%E7%9A%84%E5%AD%97%E6%AF%8D%E7%BB%84%E5%90%88.html
视频讲解:https://www.bilibili.com/video/BV1yV4y1V7Ug

第一印象:

让我先思考,ok,我先回家做口饭然后就思考。现在看的话觉得两层回溯??

等会到家再看哈

吃饱了,我的猪肉炖粉条做的还行啊,真是天才,下次少放点油就行。

这道题给出的是字符串,不再是连续的数字,需要把字符串拆开先。

观察例子,比如数字23,那么答案的组合是 一个字母来自2,一个字母来自3,组合在一起的答案,不包括只来自2的,只来自3的,还有22,33的情况。

我大概知道怎么做出选数字的,但是怎么选字母呢??我有点实现不出来,我觉得我的代码实现能力比算法思路要弱。

直接看题解吧

看完题解的思路:

明白了,画的图应该是
请添加图片描述

这里要注意,之前的题目是在一个集合里面搜索,startIndex是下一次搜索的位置。而这道题是两个集合,不需要startIndex。

返回值和参数:

返回值还是经典的void,参数肯定有题目中的string digits,还需要一个int index 来记录遍历到字符串里的哪个位置了。

终止条件:

终止条件就是字符串遍历完了。

if (index == digits.size()) {
    result.push_back(s);
    return;
}

单层递归逻辑:

这里我觉得是这道题最难的,我没能在看完题解之后自己做出来,我照着题解做一遍试试。

我觉得我对字符串的操作掌握的不好。

首先到map中 找到当前的数字 所对应的字母们(字符串)。

int digit = digits[index] - '0';        // 将index指向的数字转为int
string letters = letterMap[digit];      // 取数字对应的字符集

然后在这些字母的集合里进行for循环的操作

for (int i = 0; i < letters.size(); i++) {
    s.push_back(letters[i]);            // 处理
    backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了
    s.pop_back();                       // 回溯
}

这样去看这道题就清晰一些了,操作的集合是每个数字所对应的字母们(字符串)。 是很多个集合的操作,而不是像之前的题只在一个集合里。

只在一个集合里需要startIndex来标识下一次搜索的位置。而这样很多个集合,就是把另一个集合(比如数字2对应的“abc”)直接拿过来做了。但是这个集合拿过来的过程,是从数字串那一一对应过来的。

数字串是一个一个数字遍历的(比如23),就需要index来标明遍历到哪个了,这个过程在树形图里很明显。

其实一个数字对应的字母的数量是这个树的宽度

有多少个数字是这个树的高度。

写到这我觉得我捋清了这道题的思路,希望下次做能直接做出来。就是字符串的操作我有点陌生。

//每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder()

他用stringBuilder去收集一个个结果,最后放在result里。

这道题没剪枝哈哈哈哈

实现中的困难:

我没能自己做出来,遇到字符串的操作就懵了,而且对不同集合的for循环操作也很懵

感悟:

不同集合的回溯,首先找到不同集合(树形图很适合理解)

再去操作那个for循环

代码:

class Solution {
     List<String> list = new ArrayList<>();


    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        String[] numberList = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backtracking(digits, numberList, 0);
        return list;
    }

    //每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder();

    private void backtracking(String digits, String[] numberList, int index) {
        if (index == digits.length()) {
            list.add(temp.toString());
            return;
        }

        String curStr = numberList[digits.charAt(index) - '0'];
        for (int i = 0; i < curStr.length(); i++) {
            temp.append(curStr.charAt(i));
            backtracking(digits, numberList, index + 1);
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值