代码随想录算法训练营第二十五天 | 216. 组合总和III, 17. 电话号码的字母组合

216. 组合总和III

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

重点:

1. 递归+回溯的模板

2. 回溯的剪枝

思路:

递归+回溯:

1. 确定参数及返回值

两个全局变量path和result,还要需要sum记录当前的和,startIndex记录从哪个开始

public void backtracking(int k, int n, int sum, int startIndex)

2. 确定终止条件

当path记录到了k个时,检查当前的sum是否满足条件

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

3. 确定单层递归和回溯的逻辑

for循环控制每层的循环的1-9的哪一个,接着再递归下去就行

剪枝操作在函数开头,sum超过n了,直接return

还有for循环的限制也可以剪枝,因为如果后面的数并不能满足还差k几个的个数,就没必要for循环后面的数了

// path.size() 现在收集了几个
// k - path.size() 差路径所需要的个数几个
// 9 - (k - path.size()) + 1 至多直到哪个,加1是为了下标对齐,因为下标是1开始
// 想象一下,如果path.size()为2,k为5,我们还差3个没收集,
// 9减去还差几个没收集再加1的就知道我们最多可以取到哪个位置
// 比如9-3+1=7,说明最多能到7,到了8我们就取不到3个的了,8只能取[8,9]
for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
    sum += i;
    path.add(i);
    // 递归到下一层
    backtracking(k, n, sum,i + 1);
    // 回溯到当前层以便进行另一个分枝的递归
    sum -= i;
    path.remove(path.size() - 1);
}
List<List<Integer>> result;
List<Integer> path;

public List<List<Integer>> combinationSum3(int k, int n) {
    result = new ArrayList<>();
    path = new ArrayList<>();
    backtracking(k, n, 0,1);
    return result;
}

public void backtracking(int k, int n, int sum, int startIndex) {
    // 剪枝1
    if (sum > n) {
        return;
    }
    // 终止条件
    if (path.size() == k) {
        if (sum == n) {
            result.add(new ArrayList<>(path));
        }
        return;
    }
    // 剪枝2
    // path.size() 现在收集了几个
    // k - path.size() 差路径所需要的个数几个
    // 9 - (k - path.size()) + 1 至多直到哪个,加1是为了下标对齐,因为下标是1开始
    // 想象一下,如果path.size()为2,k为5,我们还差3个没收集,
    // 9减去还差几个没收集再加1的就知道我们最多可以取到哪个位置
    // 比如9-3+1=7,说明最多能到7,到了8我们就取不到3个的了,8只能取[8,9]
    for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
        sum += i;
        path.add(i);
        // 递归到下一层
        backtracking(k, n, sum,i + 1);
        // 回溯到当前层以便进行另一个分枝的递归
        sum -= i;
        path.remove(path.size() - 1);
    }
}

17. 电话号码的字母组合

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

题目链接/文章讲解:代码随想录

视频讲解:还得用回溯算法!| LeetCode:17.电话号码的字母组合_哔哩哔哩_bilibili

重点:

1. Java对String频繁操作的时候,可以用StringBuilder

2. 递归+回溯

3. 数字和字母的映射

思路:

递归+回溯法:

1. 确定参数及返回值

用一个index来记录,当前for循环的digits中的哪一个

private void backtracking(String digits, int index)

2. 确定终止条件

最后一个digit也遍历完了

if (index == digits.length()) {
    result.add(s.toString());
    return;
}

3. 确定单层递归的逻辑

先获取到当前digit对应了哪些字母,再for循环这些字母。for循环中再递归调用下一个digit来达到嵌套N层for循环的目的,最后还要回溯到当前digit

// 获取当前digit对应哪些字母
int curDigit = digits.charAt(index) - '0';
String letter = letterMap[curDigit];
// for循环当前digit对应的那些字母
// 再用递归来嵌套下一个for循环
// 回溯回当前层时要删掉最后一个
for (int i = 0; i < letter.length(); i++) {
    s.append(letter.charAt(i));
    backtracking(digits, index + 1);
    s.deleteCharAt(s.length() - 1);
}
List<String> result;
// 当需要不断对String进行操作时,可以考虑用StringBuilder
StringBuilder s;
// 下标为0-9,对应的内容为各自的字母
String[] letterMap = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
public List<String> letterCombinations(String digits) {
    result = new ArrayList<>();
    if (digits == null || digits.length() == 0) {
        return result;
    }
    s = new StringBuilder();
    backtracking(digits, 0);
    return result;
}

// 这里的index表示我们递归到了哪个digit了
private void backtracking(String digits, int index) {
    // 终止条件
    // 最后一个digit也全都遍历完了,所以index应该超出digit的范围
    if (index == digits.length()) {
        result.add(s.toString());
        return;
    }
    // 获取当前digit对应哪些字母
    int curDigit = digits.charAt(index) - '0';
    String letter = letterMap[curDigit];
    // for循环当前digit对应的那些字母
    // 再用递归来嵌套下一个for循环
    // 回溯回当前层时要删掉最后一个
    for (int i = 0; i < letter.length(); i++) {
        s.append(letter.charAt(i));
        backtracking(digits, index + 1);
        s.deleteCharAt(s.length() - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值