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

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

216.组合总和III

题目简介:

找出所有相加之和为 nk 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次

返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

示例 1:

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

示例 2:

输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
解释:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
没有其他符合的组合了。

示例 3:

输入: k = 4, n = 1
输出: []
解释: 不存在有效的组合。
在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。

个人思路:

简单暴力法,无法去嵌套n(动态)层for循环,只能考虑使用递归

回溯三部曲

  • 确定参数和返回值
    • 参数:基本的k、n和startIndex、sum
    • 返回值:void
  • 确定递归结束条件:list.size == k 时,若还满足sum == n,则保存结果
  • 确定单层递归逻辑及剪枝:
for (int i = startIndex; i <= 10 - k + list.size() && list.size() < k; i++) {//根据个数限制剪枝
    if (sum + i > n) //剪枝操作,发现不符直接跳出循环,不必执行下面的操作
        break;
    list.add(i);
    backtracking(k, n, i + 1, sum + i);
    list.remove(list.size() - 1);//回溯操作
}

完整代码:

class Solution {
    public List<List<Integer>> result = new ArrayList<>();
    public List<Integer> list = new ArrayList<>();

    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 (list.size() == k && sum == n) {
            result.add(new ArrayList<>(list));
            return;
        }
        //两个剪枝
        //1.往后递归数量也不够k个(列个方程即可
        //2.当前递归sum已经大于n,没必要再往下了
        for (int i = startIndex; i <= 10 - k + list.size() && list.size() < k; i++) {
            if (sum + i > n)
                break;
            list.add(i);
            backtracking(k, n, i + 1, sum + i);
            list.remove(list.size() - 1);
        }
    }
}

17.电话号码的字母组合

题目简介:

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

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

img

示例 1:

输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]

示例 2:

输入:digits = ""
输出:[]

示例 3:

输入:digits = "2"
输出:["a","b","c"]

个人思路:

同样是模拟n层循环,只能考虑使用回溯法

这道题8个数字,有的数字对应3个字母有的对应4个字母,直接函数对应ASCII码比较困难,所以考虑使用map表存储

回溯三部曲:

  • 确定参数和返回值:
    • 参数:char数组,StringBuild 临时字符串,得到每个结果进行保存
    • 返回值:void
  • 确定递归结束条件:数组长度为0就结束,同时StringBuild的长度不为0才保存结果
  • 确定单层递归逻辑:
    • 先确定for循环的范围(4或3)
    • for循环中根据hash取对应字母,进行回溯操作即可
class Solution {
    public List<String> result = new ArrayList<>();
    public HashMap<Integer, Character> hashMap = new HashMap<>();

    public List<String> letterCombinations(String digits) {
        initHash();
        backtracking(digits.toCharArray(), new StringBuilder());
        return result;
    }

    public void backtracking(char[] str, StringBuilder stringTemp) {
        if (str.length == 0) {
            if (stringTemp.length() != 0)//避免添加空白字符串
                result.add(new String(stringTemp.toString()));
            return;
        }
        int num = str[0] == '7' || str[0] == '9' ? 4 : 3;
        for (int j = 1; j <= num; j++) {              //每个数字最多对应4个字符
            Character temp = hashMap.get((str[0] - '0') * 10 + j);
            stringTemp.append(temp);
            backtracking(Arrays.copyOfRange(str, 1, str.length), stringTemp);
            stringTemp.deleteCharAt(stringTemp.length() - 1);
        }
    }

    //初始化Hash表
    public void initHash() {
        char letter = 'a';
        for (int i = 2; i <= 6; i++) {
            for (int j = 1; j <= 3; j++) {
                hashMap.put(i * 10 + j, letter++);
            }
        }
        for (int i = 1; i <= 4; i++)
            hashMap.put(70 + i, letter++);
        for (int i = 1; i <= 3; i++)
            hashMap.put(80 + i, letter++);
        for (int i = 1; i <= 4; i++)
            hashMap.put(90 + i, letter++);
    }
}

题解解析:

基本思路都是使用Hash加回溯,但相比自己的代码还是优化了不少

  • Hash表上的优化,使用一个String一维数组替代HashMap,提高效率,节省空间
  • 回溯参数上的优化,传入数组和下标值,避免了本人代码中每次复制数组的操作
  • 隐式回溯的优化,在传参字符串中,可以传入s+digits[i];
class Solution {
    public List<String> result = new ArrayList<>();
    public 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.toCharArray(), 0, new StringBuilder());
        return result;
    }

    public void backtracking(char[] str, int index, StringBuilder stringTemp) {
        if (str.length == index) {
            result.add(new String(stringTemp.toString()));//也可以将stringTemp设为全局变量,这里就不必new一个一样的
            return;
        }
        String s = array[str[index] - '2'];
        for (int j = 0; j < s.length(); j++) {              //每个数字最多对应4个字符
            char[] chars = s.toCharArray();
            stringTemp.append(chars[j]);
            backtracking(str, index + 1, stringTemp);
            stringTemp.deleteCharAt(stringTemp.length() - 1);
        }
    }
}
        char[] chars = s.toCharArray();
        stringTemp.append(chars[j]);
        backtracking(str, index + 1, stringTemp);
        stringTemp.deleteCharAt(stringTemp.length() - 1);
    }
}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值