代码随想录算法训练营第二十五天 | 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);
        }
    }
}
相关文章
|
5天前
|
机器学习/深度学习
leetcode代码记录(旋转图像
leetcode代码记录(旋转图像
10 0
|
5天前
|
算法
leetcode代码记录(全排列 II
leetcode代码记录(全排列 II
13 4
|
5天前
|
算法
leetcode代码记录(全排列
leetcode代码记录(全排列
12 1
|
5天前
|
索引
leetcode代码记录(Z 字形变换
leetcode代码记录(Z 字形变换
11 1
|
5天前
leetcode代码记录(最长回文子串
leetcode代码记录(最长回文子串
10 2
|
5天前
|
算法 C++
【刷题】Leetcode 1609.奇偶树
这道题是我目前做过最难的题,虽然没有一遍做出来,但是参考大佬的代码,慢慢啃的感觉的真的很好。刷题继续!!!!!!
9 0
|
5天前
|
算法 索引
【刷题】滑动窗口精通 — Leetcode 30. 串联所有单词的子串 | Leetcode 76. 最小覆盖子串
经过这两道题目的书写,相信大家一定深刻认识到了滑动窗口的使用方法!!! 下面请大家继续刷题吧!!!
12 0
|
5天前
|
算法
【刷题】 leetcode 面试题 08.05.递归乘法
递归算法是一种在计算机科学和数学中广泛应用的解决问题的方法,其基本思想是利用问题的自我相似性,即将一个大问题分解为一个或多个相同或相似的小问题来解决。递归算法的核心在于函数(或过程)能够直接或间接地调用自身来求解问题的不同部分,直到达到基本情况(也称为基础案例或终止条件),这时可以直接得出答案而不必再进行递归调用。
25 4
【刷题】 leetcode 面试题 08.05.递归乘法
|
5天前
|
存储 算法 安全
【刷题】 leetcode 面试题 01.06 字符串压缩
来看效果: 非常好!!!过啦!!!
27 5
【刷题】 leetcode 面试题 01.06 字符串压缩