代码随想录算法训练营(JAVA)| 第七章 回溯算法part02

 

      今日任务 

力扣 216. 组合总和 III17. 电话号码的字母组合

题目 :216. 组合总和 III

思路

k决定了树的深度

判断dfs退出的条件:数组之和为 n


回溯三部曲

  • 确定递归函数参数

这里我定义path 和 result为全局变量。

至于为什么取名为path?从上面树形结构中,可以看出,结果其实就是一条根节点到叶子节点的路径。

接下来还需要如下参数:

  • targetSum(int)目标和,也就是题目中的n。

  • k(int)就是题目中要求k个数的集合。

  • sum(int)为已经收集的元素的总和,也就是path里元素的总和。

  • startIndex(int)为下一层for循环搜索的起始位置。

所以代码如下:

 public void dfs (int k, int n, int startIdx, int sum)

其实这里sum这个参数也可以省略,每次targetSum减去选取的元素数值,然后判断如果targetSum为0了,说明收集到符合条件的结果了,我这里为了直观便于理解,还是加一个sum参数。

还要强调一下,回溯法中递归函数参数很难一次性确定下来,一般先写逻辑,需要啥参数了,填什么参数。

  • 确定终止条件

所以如果path.size() 和 k相等了,就终止。

如果此时path里收集到的元素和(sum) 和targetSum(就是题目描述的n)相同了,就用result收集当前的结果。

所以 终止代码

if (path.size() == k) {
            ans.add(new ArrayList<>(path));
            return;
}
  • 单层搜索过程
  • 216.组合总和III

 

处理过程就是 path收集每次选取的元素,相当于树型结构里的边,sum来统计path里元素的总和。

for (int i = startIdx; i <= 9; i++ ) {
    path.add(i);
    sum += i;
    dfs(k, n, i + 1, sum);
    sum -= i;
    path.removeLast();
}

别忘了处理过程 和 回溯过程是一一对应的,处理有加,回溯就要有减!

剪枝
if (sum > n) return;
if (path.size() > k) return;
​
if (sum == n && path.size() == k) {
    ans.add(new ArrayList<>(path));
    return;
}

题解

class Solution {
    LinkedList<Integer> path = new LinkedList<>();
    List<List<Integer>> ans = new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        dfs(k, n, 1, 0);
        return ans;
    }

    public void dfs (int k, int n, int startIdx, int sum) {
        if (sum > n) return;
        if (path.size() > k) return;

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

        for (int i = startIdx; i <= 9; i++ ) {
            path.add(i);
            sum += i;
            dfs(k, n, i + 1, sum);
            sum -= i;
            path.removeLast();
        }
    }
}

题目 :17. 电话号码的字母组合

思路

  

组合型枚举

首先对2-9数字对应的字母进行存储

依据输入的数字个数决定树的深度


回溯三部曲:

  • 确定回溯函数参数

首先需要一个字符串s来收集叶子节点的结果,然后用一个字符串数组result保存起来,这两个变量我依然定义为全局。

再来看参数,参数指定是有题目中给的string digits,然后还要有一个参数就是int型的index。

这个index是记录遍历第几个数字了,就是用来遍历digits的(题目中给出数字字符串),同时index也表示树的深度。

public void dfs(String digits, String[] numString, int num)
  • 确定终止条件

例如输入用例"23",两个数字,那么根节点往下递归两层就可以了,叶子节点就是要收集的结果集。

那么终止条件就是如果index 等于 输入的数字个数(digits.size)了(本来index就是用来遍历digits的)。

然后收集结果,结束本层递归。

if (num == digits.length()) {
    list.add(temp.toString());
    return;
}
  • 确定单层遍历逻辑

首先要取index指向的数字,并找到对应的字符集(手机键盘的字符集)。

然后for循环来处理这个字符集,代码如下:

String str = numString[digits.charAt(num) - '0'];

for (int i = 0; i < str.length(); i++ ) {
    temp.append(str.charAt(i));
    dfs(digits, numString, num + 1);

    temp.deleteCharAt(temp.length() - 1);
}

注意这里for循环,都是求同一个集合中的组合!

注意:输入1 * #按键等等异常情况

代码中最好考虑这些异常情况,但题目的测试数据中应该没有异常情况的数据,所以我就没有加了。

但是要知道会有这些异常,如果是现场面试中,一定要考虑到!

题解

class Solution {
    // 定义一个全局的List来存储所有可能的字母组合
    List<String> list = new ArrayList<>();

    public List<String> letterCombinations(String digits) {
        // 如果输入的字符串为空,直接返回空列表
        if (digits == null || digits.length() == 0) {
            return list;
        }
        // 对应数字到字母的映射,注意0和1不对应任何字母
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        // 开始深度优先搜索
        dfs(digits, numString, 0);
        return list;
    }

    // 用于构建当前遍历到的字符串
    StringBuilder temp = new StringBuilder();

    // 深度优先搜索函数
    public void dfs(String digits, String[] numString, int num) {
        // 如果当前的长度和digits长度相等,说明找到了一个组合
        if (num == digits.length()) {
            list.add(temp.toString());
            return;
        }

        // 获取当前数字对应的所有可能字母
        String str = numString[digits.charAt(num) - '0'];
        // 遍历这些字母
        for (int i = 0; i < str.length(); i++ ) {
            // 将当前字母加到构建中的字符串
            temp.append(str.charAt(i));
            // 继续深度优先搜索下一个数字
            dfs(digits, numString, num + 1);
            // 回溯,移除最后一个字符,尝试下一个可能的字母
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值