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

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

216.组合总和III

力扣题目链接(opens new window)

找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。

说明:

  • 所有数字都是正整数。
  • 解集不能包含重复的组合。

示例 1: 输入: k = 3, n = 7 输出: [[1,2,4]]

示例 2: 输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]]

思路:回溯

回溯法适用于从n个数中,找出符合条件的集合,此题满足回溯条件
此题要求只使用数字1到9,那么结果集一定在【1,2,3,4,5,6,7,8,9】中,将其作为根节点递归遍历

那么1到9就是根节点的9个子树,用for循环遍历

每一次遍历确保递归传递的结果集大于i,这样不会有重复参数存在

回溯法三要素
方法参数和返回值:参数 int k,int n,int startindex 返回值void
中止条件:

k表示树的深度,递归达到深度k,结果集元素的数量才满足提议,所以当path.size == k,判断结果集元素综合等于n,如果等于,则加入result
核心逻辑

对【1,2,3,4,5,6,7,8,9】for循环遍历,遍历起始值由startIndex决定,startIndex初始等于1

216.组合总和III

递归时每次给startIndex+1
回溯取出path最后一个元素

代码如下

// 时间复杂度:分析回溯问题的时间复杂度,有一个通用公式:路径长度×搜索树的叶子数。对于本题,它等于 O(k⋅C(9,k))
// 空间复杂度:O(n+k)=O(n),即递归使用栈空间的空间代价和临时数组    
List<Integer> path = new ArrayList<>();
List<List<Integer>> result = new ArrayList<>();
int sum = 0;
public List<List<Integer>> combinationSum3(int k, int n) {
    backTracking(k, n, 1);
    return result;
}

public void backTracking(int k, int n, int startIndex) {
    if(sum > n)
        return;
    if (path.size() == k) {// 终止条件
        int sum = 0;
        for (int i = 0; i < path.size(); i++) {
            sum = sum + path.get(i);
        }
        if (sum == n) {
            result.add(new ArrayList<>(path));
        }
        return;
    }

    for (int i = startIndex; i <= 9 - k + path.size() + 1; i++) {
        path.add(i);
        backTracking(k, n, i + 1);
        path.remove(path.size() - 1);
    }
}

剪枝 优化

我只想到了在添加限制for循环的范围,i <= 9 - (k - path.size()) + 1
可以从sum入手,如果sum > n,由于数字都是整数,遍历的结果只会越来越大,没必要遍历下去了
代码如下

public void backTracking(int k, int n, int startIndex) {
       if(sum > n)
           return;

17.电话号码的字母组合

力扣题目链接(opens new window)

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

17.电话号码的字母组合

示例:

  • 输入:“23”
  • 输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

说明:尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。

思路:回溯

将数字入参转化字符串。例如“234”转化为“abcdefghi"
abc不能相互组合,def不能相互组合,ghi不能相互组合
但abc取出其中一个,可以和def取出其中一个,ghi取出其中一个互相组合
首先定义字符串数组,来完成数字和字符串的映射
String[] array = {“”, “”, “abc”, “def”, “ghi”, “jkl”, “mno”, “pqrs”, “tuv”, “wxyz”};
定义index = 0; index跟前面startIndex作用不一致,它用来表示第几个数字,从【字符串digits取出数字】,也可作为中止条件

回溯法三要素
方法入参和返回值 入参:String digits,int index 返回值 void
中止条件 index == 数组字符串的长度
核心逻辑
1.for循环对当前数字映射字符串遍历,比如2对应"abc"
2.递归 :stringbuilder添加字符,比如"a".将index+1,传入递归方法中,便于找到下一个数字对应字符串
3.回溯:stringbuilder去除字符,比如"ad"变为"a"

代码如下

// 时间复杂度:分析回溯问题的时间复杂度,有一个通用公式:路径长度×搜索树的叶子数。对于本题2,3,4,5,6,8数字对应三个字符,7,9对应四个字符。假设m为三个字符数字出现次数,n为四个字符数字出现次数,那么不同的字母组合一共有 O(3^m * 4^n) 而路径长度固定在0-9之间,可当作常数,所以时间复杂度为O(3^m * 4^n)
// 空间复杂度:O(3^m * 4^n),    3^m * 4^n是搜索树结点的数量。递归的深度最多为8层,每递归一层,结果集的字母数量+1,那么结果集最多有8个字符。很明显O(3^m * 4^n) 大于O(递归栈深度),所以空间复杂度为O(3^m * 4^n)
List<String> result = new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
String[] array = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};

public List<String> letterCombinations(String digits) {
    if(digits == null || digits.length() == 0)
        return result;
    backTracking(digits, 0);
    return result;
}

public void backTracking(String digits, int index) {
    if (index == digits.length()) {//中止条件:遍历过数字数量,等于digits的数字数量
        result.add(stringBuilder.toString());
        return;
    }

    String str = array[digits.charAt(index) - '0'];// 当前数字对应的字符串
    for (int i = 0; i < 3; i++) {// digits.charAt(index) 数字,对应字符串遍历

        stringBuilder.append(str.charAt(i));
        backTracking(digits, index + 1);
        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
    }

}

问题

忘记处理边缘情况了.如果不对digits边缘情况判断
会导致出错
在这里插入图片描述

解决方式如下

public List<String> letterCombinations(String digits) {
        if(digits == null || digits.length() == 0)
            return result;
        backTracking(digits, 0);
        return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值