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

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

1. LeetCode 216. 组合总和 III

1.1 思路

  1. 这题与77.组合的区别在于给我们的是一个和为n的限制,集合固定在[1,9]。
  2. 思路和77.组合大概相同,同样是通过回溯算法的递归帮我们控制for循环的嵌套层数,也是抽象成一个树形结构。这里的取数之后,剩下的集合就不包括前面取过的数,比如取1后剩下集合[2,9],取2后剩下集合[3,9],为什么没了1呢?因为我们求得是组合不是排列。我们如何控制这个集合范围呢?通过startIndex来控制。而我们的树的深度是由k来控制的,取几个元素就是递归几层。树的宽度是由最初的集合大小9来控制的。
  3. 定义全局变量path来收集结果,然后result放入多个path结果,因为结果可能不止一个
  4. 回溯函数的参数和返回值:返回值void,参数targetSum也就是n,k,sum收集路径已有的和,startIndex
  5. 终止条件:如果path.size()==k说明已经取够数作为组合了,因为结果在叶子节点上,那么可以判断sum是否等于targetSum,等于说明path路径符合就可以把其加入result然后返回了
  6. 单层搜索的逻辑:i从startIndex开始,范围为i<=9,取了数i之后就可以用sum加上。然后把数i加入到path中。然后就回溯了,把参数传入,其中的startIndex为i+1。然后是回溯的过程,要把sum减去数i,然后path弹出数i,比方说这一层已经取过12了,如何取到13呢?就是先把2弹出,再去取3,因此我们要先把数2弹出才可

1.2 代码

//
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,1,0);
        return result;
    }
    public void backtracking(int k,int n,int startIndex,int sum){
        if(path.size()==k){
            if(sum==n){
                result.add(new ArrayList(path));
            }
            return;
        }
        for(int i=startIndex;i<=9;i++){
            path.add(i);
            sum+=i;
            backtracking(k,n,i+1,sum);
            path.removeLast();
            sum-=i;
        }
    }
}

1.3 剪枝优化思路

  1. 在每一层中如果此时sum已经大于targetSum就没有继续递归取数的必要了,已经不可能符合,因此可以直接return
  2. 然后是for循环的优化:当前递归里组合已经有path.size()个元素了,题目需要k个,就还需要k-path.size()个,那我们还需要取的元素至多可以让你从9-(k-path.size())+1个位置上开始取了,+1是为了把下标补齐。比如我们此时要取k=3个数,已经取了1个了,还剩2个,那么此时至多可以从9-(3-1)+1=8,这个位置开始取[8,9]还剩下2个元素。那么9-(k-path.size())+1这个就是作为i的范围了

1.4 代码

//
class Solution {
  List<List<Integer>> result = new ArrayList<>();
  LinkedList<Integer> path = new LinkedList<>();
  public List<List<Integer>> combinationSum3(int k, int n) {
    backTracking(n, k, 1, 0);
    return result;
  }
  private void backTracking(int targetSum, int k, int startIndex, int sum) {
    // 减枝
    if (sum > targetSum) {
      return;
    }
    if (path.size() == k) {
      if (sum == targetSum) result.add(new ArrayList<>(path));
      return;
    }
    // 减枝 9 - (k - path.size()) + 1
    for (int i = startIndex; i <= 9 - (k - path.size()) + 1; i++) {
      path.add(i);
      sum += i;
      backTracking(targetSum, k, i + 1, sum);
      //回溯
      path.removeLast();
      //回溯
      sum -= i;
    }
  }
}

2. LeetCode 17. 电话号码的字母组合

2.1 思路

  1. 首先要解决的问题是题目映射的问题,我们输入的虽然是字符串,但这个字符串是一个数字,用这个数字到字符串要做个映射,我们可以用map也可以用二维数组做映射,初始化数组letterMap长度为10,下标2到9这8个位置就是对应的字符串(字符串本身也是个数组,因此是二维的)。
  2. 我们可以通过for循环来遍历,但由于给的数字长度是不确定的,用几个for循环就不好控制了,就可以通过回溯算法的递归来控制for循环的嵌套次数
  3. 因此我们可以抽象为一个树形结构,树的深度就是数字的长度,树的宽度是由每一个数字所对应的字母的长度来控制的。结果就是都在叶子节点上,也就是递归结束的地方
  4. 定义两个全局变量,str用来收集单个结果,result是个字符串数组,用来收集所有结果
  5. 回溯函数的参数和返回值:返回值void,参数就是数字字符串digits,index表示我传入的字符串遍历到哪里了。为什么这题不像上面的一样弄个startIndex呢?因为这题我们不是在同一个集合里求元素,而是多个的,因此不需要控制集合里我们遍历过哪些元素。
  6. 终止条件:当index==digits.length()时即这个数字字符串的最后一位的下一位,如果是最后一位的话我们还应该要进行处理的,所以是最后一位的下一位。然后就收获叶子节点的结果,result.add(str.toString())然后return。
  7. 单层搜索的逻辑:int digit=digits[index]-'0',因为本身是个字符,因此减去字符0才变成一个数字,然后获取到字符串就是String letter=letterMap[digit],然后在for循环里取字符,就正常的遍历顺序,然后要在str里存放我们取的字符,str.append(letter.charAt(i)),然后是下一层递归backtracking(digits,index+1),然后就是回溯了,要把当前位置的元素弹出,str.deleteCharAt(str.length()-1),因为我们要继续要右边取数遍历
  8. 本题不需要剪纸,因为要的就是所有可能的组合

2.2 代码

//
class Solution {
    //设置全局列表存储最后的结果
    List<String> list = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if (digits == null || digits.length() == 0) {
            return list;
        }
        //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        //迭代处理
        backTracking(digits, numString, 0);
        return list;
    }
    //每次迭代获取一个字符串,所以会设计大量的字符串拼接,所以这里选择更为高效的 StringBuild
    StringBuilder temp = new StringBuilder();
    //比如digits如果为"23",num 为0,则str表示2对应的 abc
    public void backTracking(String digits, String[] numString, int num) {
        //遍历全部一次记录一次得到的字符串
        if (num == digits.length()) {
            list.add(temp.toString());
            return;
        }
        //str 表示当前num对应的字符串
        String str = numString[digits.charAt(num) - '0'];
        for (int i = 0; i < str.length(); i++) {
            temp.append(str.charAt(i));
            //c
            backTracking(digits, numString, num + 1);
            //剔除末尾的继续尝试
            temp.deleteCharAt(temp.length() - 1);
        }
    }
}
相关文章
|
4月前
|
算法
Leetcode 初级算法 --- 数组篇
Leetcode 初级算法 --- 数组篇
58 0
|
6月前
|
算法
【算法】滑动窗口——找到字符串中所有字母异位词
【算法】滑动窗口——找到字符串中所有字母异位词
|
3月前
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
|
4月前
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
47 2
|
4月前
|
算法
条件运算符与条件if的姻缘,打擂台算法和大小写字母转换,if逻辑避坑
条件运算符与条件if的姻缘,打擂台算法和大小写字母转换,if逻辑避坑
44 1
|
6月前
|
算法
测试工程师的技能升级:LeetCode算法挑战与职业成长
这篇文章通过作者亲身体验LeetCode算法题的过程,探讨了测试工程师学习算法的重要性,并强调了算法技能对于测试职业成长的必要性。
101 1
测试工程师的技能升级:LeetCode算法挑战与职业成长
|
6月前
|
存储 算法 Java
LeetCode经典算法题:打家劫舍java详解
LeetCode经典算法题:打家劫舍java详解
92 2
|
6月前
|
人工智能 算法 Java
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
LeetCode经典算法题:井字游戏+优势洗牌+Dota2参议院java解法
68 1
|
5月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
6月前
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
145 2