代码随想录第25天|216.组合总和III ​​​​​​​,17. 电话号码的字母组合

216.组合总和III 

回溯三部曲

  • 确定递归函数参数
  • targetSum(int)目标和,也就是题目中的n。
  • k(int)就是题目中要求k个数的集合。
  • sum(int)为已经收集的元素的总和,也就是path里元素的总和。
  • startIndex(int)为下一层for循环搜索的起始位置。
  • 确定终止条件
  • k其实就已经限制树的深度,因为就取k个元素,树再往下深了没有意义。

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

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

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

在纸上模拟了一下,例如到path:[1,2,3,4]的时候开始回溯

class Solution {
     List<List<Integer>> res=new ArrayList<>();
     LinkedList<Integer> path=new LinkedList<>();
    // n:目标和,也就是题目中的n。
    // k:题目中要求k个数的集合。
    // sum:已经收集的元素的总和,也就是path里元素的总和。
    // startIndex:下一层for循环搜索的起始位置
    public List<List<Integer>> combinationSum3(int k, int n) {
       // 只使用数字1到9
       backtracking(k,n,0,1);
       return res;

    }
    void backtracking(int k, int n,int sum,int startIndex){
        //终止条件
        if(path.size()==k&&sum==n){
            res.add(new ArrayList<>(path));
            //如果这里是res.add(path),那么path其实都是[]
            return;
        }
        //剪枝
        if(sum>n){//这段没有回导致栈溢出
            return;
        }
        //处理逻辑,横向搜索
        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.removeLast();//移除最新进来的元素
            

        }

    }
}

  for(int i=startIndex;i<=9-(k-path.size())+1;i++){//剪枝条件理解重点

遍历的范围是可以剪枝优化的,怎么优化呢?

来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了(不足k个)。 在第二层for循环,从元素3开始的遍历都没有意义了(不足k个)。如图

 

图中每一个节点(图中为矩形),就代表本层的一个for循环,那么每一层的for循环从第二个数开始遍历的话,都没有意义,都是无效遍历。

所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置

接下来看一下优化过程如下:

  1. 已经选择的元素个数:path.size();

  2. 还需要的元素个数为: k - path.size();

  3. 在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历

为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。

举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。

从2开始搜索都是合理的,可以是组合[2, 3, 4]。

17. 电话号码的字母组合

没有思路啊丢

首先要遍历digits的每一个数字,一个数字对应一个字符串str,如digits=“23”,可以得到str1=“abc”,str2=“def”,我们求str1和str2的所有组合

树型结构:digits的长度为树的深度,每一层的宽度取决于对应str的字符个数

 

每次调用 backTracking就相当于遍历digits的下一个数字

class Solution {
  //设置全局列表存储最后的结果
    List<String> res = new ArrayList<>();
    public List<String> letterCombinations(String digits) {
        if(digits.length()==0){
            return res;
        }
        //组合问题
        String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        backTracking(digits,numString,0);
        return res;

    }
    StringBuilder sb=new StringBuilder();
    public void backTracking(String digits,String[] numString,int num){
        //遍历digits全部数字对应的字符串一次 记录一次得到的字符串
        if(num==digits.length()){
            res.add(sb.toString());
            return;
        }
        //str表示当前num对应的字符串
        String str=numString[digits.charAt(num)-'0'];
        for(int i=0;i<str.length();i++){
            sb.append(str.charAt(i));
            backTracking(digits,numString,num+1);
            //回溯,剔除末尾的继续尝试
            sb.deleteCharAt(sb.length()-1);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值