代码随想录刷题笔记 DAY 25 | 组合问题 No.77 | 组合求和III No.216 | 电话号码的字母组合 No.17

文章目录

    • Day 25
      • 01. 组合问题(No. 77)
        • 2.1 题目
        • 2.2 笔记
        • 2.3 代码
      • 02. 组合求和III(No. 216)
        • 2.1 题目
        • 2.2 笔记
        • 2.3 代码
      • 03. 电话号码的字母组合(No. 17)
        • 3.1 题目
        • 3.2 笔记
        • 3.3 代码
        • 3.4 补充

Day 25

01. 组合问题(No. 77)

2.1 题目

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

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

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n
2.2 笔记

本题的常规解法在昨天的题解中已经写出

代码随想录刷题笔记 DAY 24 | 回溯算法理论基础 | 组合问题 No. 77

这里来讲解一下本题的剪枝优化

假设 n = 4k = 4

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

for (int i = startIndex; i <= n; i++) {
    path.add(i);
    backtracking(n, k, i+1);
    path.remove(path.size() - 1); // 回溯删除节点
}

startIndex == 2 的时候,后面能取得的数字为 3 4 即遍历完所有的取值数目也不可能达到题目要求的 k

所以对上面除了 1 2 3 4 这条线都应该通过剪枝去除,因为遍历它们没有意义。

当确定了一个 i 那这条路线能取得的最多的数字个数就确认了,也就是 n - i + 1

当取到这个节点的时候 path 内共有 path.size() 个元素,所以从这个路线中能取得的 最大 数目为
N m = n − i + 1 − p a t h . s i z e ( ) N_m = n - i + 1 - path.size() Nm=ni+1path.size()
如果这条路线能够取得总数 k,那可以得出
N m ≥ k N_m \ge k Nmk
最终能够推出
i ≤ n − k + 1 − p a t h . s i z e ( ) i \le n - k + 1-path.size() ink+1path.size()
这时候的 i 才是 有意义i

所以 for 循环的终止条件应该改为上式

2.3 代码
class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combine(int n, int k) {
        backtracking(n, k, 1);
        return res;
    }
    public void backtracking(int n, int k, int startIndex) {
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i <= (n - k + path.size() + 1); i++) {
            path.add(i);
            backtracking(n, k, i+1);
            path.remove(path.size() - 1);
        }
    }
}

02. 组合求和III(No. 216)

2.1 题目

找出所有相加之和为 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,没有有效的组合。

提示:

  • 2 <= k <= 9
  • 1 <= n <= 60
2.2 笔记

如果做过组合问题,这道题目就非常容易了,思路和剪枝操作都是完全相同的,就只需要注意一下递归的终点。

因为本题有两个限制条件

  • 数字的总数是 k
  • 数字的和是 n

所以当判断出 sum > k 的时候也要记得结束递归

2.3 代码
class Solution {
    int temp = 0;
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> combinationSum3(int k, int n) {
        backtracking(k, n, 1);
        return res;
    }
    public void backtracking(int k, int n, int startIndex) {
        if (temp > n) {
            return;
        }
        if (path.size() == k && temp == n) {
            res.add(new ArrayList(path));
        }
        for (int i = startIndex; i <= (9 - k + path.size() + 1); i++) {
            path.add(i);
            temp += i;
            backtracking(k, n, i+1);
            temp -= i;
            path.remove(path.size() - 1);
        }
    }
}

03. 电话号码的字母组合(No. 17)

题目链接

代码随想录题解

3.1 题目

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

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

示例 1:

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

示例 2:

输入:digits = “”
输出:[]

示例 3:

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

提示:

  • 0 <= digits.length <= 4
  • digits[i] 是范围 ['2', '9'] 的一个数字。
3.2 笔记

先给出这道题的回溯解法:

在大多数的题目中回溯的作用就是提供一个层数可控的 for 循环来暴力解决这个问题,所以当某道题目没有思路的时候就先向 for 循环的方向思考。

❓ 这道题目用 for 循环应该如何解答呢?

💡 假如说只按了两个数字,23,首先将 数字映射为字符数组,再去求这 两个数组 的组合,那肯定很容易,两层 for 循环就可以解决。

for (String a : arr1) {
	for (String b : arr2) {		
	}
}

通过这样的形式就能求出所有的组合。

那三个按键、五个按键该怎么解决呢?

这就需要回溯法来解决这个问题了,大家遇到这道题没思路的原因很大概率是没接触过这种每层的分枝操作的 不是同一个数组

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以要做的就是在每层遍历完后去改变 for 循环中遍历的数组,这就是形成本题目要求的树形结构的方法。

可以在每次递归(前往下一层)的时候传递一个标识,用这个标识来确定 本层 遍历的数组是哪一个。

这里定为 index,从 0 开始,直到遍历完 所有的按键结束,也就是 index > 按下的按键总数

如何通过 index 来定位到遍历的是 哪些 字母呢?

  • 因为数字和字母存在着映射的关系,所以可以提前准备好所有的映射关系

    static Map<Character, String[]> NumMap = new HashMap<>();
    static {
            NumMap.put('2', new String[]{"a", "b", "c"});
            NumMap.put('3', new String[]{"d", "e", "f"});
            NumMap.put('4', new String[]{"g", "h", "i"});
            NumMap.put('5', new String[]{"j", "k", "l"});
            NumMap.put('6', new String[]{"m", "n", "o"});
            NumMap.put('7', new String[]{"p", "q", "r", "s"});
            NumMap.put('8', new String[]{"t", "u", "v"});
            NumMap.put('9', new String[]{"w", "x", "y", "z"});
        }
    
  • 接下来将输入的字符串转为字符数组,index 的意义就是这个字符数组的下标

    globalArr = digits.toCharArray();
    
  • 这样通过下标找到当前层是哪个数字,再通过 数字和字母的映射关系 就直到当前层遍历的字符串数组

3.3 代码
class Solution {
    char[] globalArr; // 存储传入的按键
    StringBuilder temp = new StringBuilder(); // 路径变量
    List<String> res = new ArrayList<>();
    static Map<Character, String[]> NumMap = new HashMap<>();
    static {
        NumMap.put('2', new String[]{"a", "b", "c"});
        NumMap.put('3', new String[]{"d", "e", "f"});
        NumMap.put('4', new String[]{"g", "h", "i"});
        NumMap.put('5', new String[]{"j", "k", "l"});
        NumMap.put('6', new String[]{"m", "n", "o"});
        NumMap.put('7', new String[]{"p", "q", "r", "s"});
        NumMap.put('8', new String[]{"t", "u", "v"});
        NumMap.put('9', new String[]{"w", "x", "y", "z"});
    }
    public List<String> letterCombinations(String digits) {
        globalArr = digits.toCharArray();
        if (globalArr.length == 0) {
            return new ArrayList<>();
        }
        backtracking(0);
        return res;
    }
    public void backtracking(int index) {
        if (index > globalArr.length - 1) {
            res.add(temp.toString());
            return;
        }
        // 获取本层遍历的是哪个字符数组
        String[] letterArr = NumMap.get(globalArr[index]);
        for (String s : letterArr) {
            temp.append(s);
            backtracking(++index);
            temp.deleteCharAt(temp.length() - 1);   
            index--;
        }
    }
}
3.4 补充

其实这道题目我一开始做的时候使用的是另一种方法。

以 按键 234 为例,本题其实就是求 23 的所有组合 x,再求 x4 的所有组合。

所以先编写一个实现两两组合的代码,再通过遍历将所有的按键全部组合起来也能得到结果。

class Solution {
    // 映射关系
    static Map<Character, String[]> NumMap = new HashMap<>();
    static {
        NumMap.put('2', new String[]{"a", "b", "c"});
        NumMap.put('3', new String[]{"d", "e", "f"});
        NumMap.put('4', new String[]{"g", "h", "i"});
        NumMap.put('5', new String[]{"j", "k", "l"});
        NumMap.put('6', new String[]{"m", "n", "o"});
        NumMap.put('7', new String[]{"p", "q", "r", "s"});
        NumMap.put('8', new String[]{"t", "u", "v"});
        NumMap.put('9', new String[]{"w", "x", "y", "z"});
    }
    public List<String> letterCombinations(String digits) {
        char[] charArray = digits.toCharArray();
        // 边界情况的处理:字符个数不足两个
        if (charArray.length == 1) {
            return Arrays.stream(NumMap.get(charArray[0])).toList();
        } else if (charArray.length == 0) {
            return new ArrayList<>();
        }
        // 将 temp 与其他字符依次两两组合
        String[] temp = getTwoCombinations(NumMap.get(charArray[0]), NumMap.get(charArray[1]));
        for (int i = 2; i < charArray.length; i++) {
            temp = getTwoCombinations(temp, NumMap.get(charArray[i]));
        }
        return Arrays.stream(temp).toList();
    }
    /**
    	实现两两组合
     */
    public String[] getTwoCombinations(String[] a, String[] b) {
        String[] res = new String[a.length * b.length];
        int n = 0;
        for (String string : a) {
            String temp = string;
            for (String s : b) {
                temp += s;
                res[n++] = temp;
                temp = string;
            }
        }
        return res;
    }
}   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/386798.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

uniapp前端手机获取安全区域css值 防止按键不能被点击

引入 再编写小程序和移动端的时候可能会出现这种情况&#xff0c;页面中的按键刚好才手机中按不到的位置 如下 这是苹果手机的home按键 如果刚好我们的按钮再这个位置,用户是点击不到的 我们就需要一个办法,能够自动的让我们的按键移动到安全可点击的区域 解决 我们可以使用…

【开源图床】使用Typora+PicGo+Gitee搭建个人博客图床

准备工作&#xff1a; 首先电脑得提前完成安装如下&#xff1a; 1. nodejs环境(node ,npm):【安装指南】nodejs下载、安装与配置详细教程 2. Picgo:【安装指南】图床神器之Picgo下载、安装与配置详细教程 3. Typora:【安装指南】markdown神器之Typora下载、安装与无限使用详细教…

nvm 安装nodejs教程【详细】

目录 一、安装nvm 二、配置镜像 三、安装nodejs 安装 查看正在用的nodejs版本 切换版本 一、安装nvm 双击安装包&#xff1a; 无脑下一步即可&#xff0c;当然你可以自定义你自己的安装目录。 安装完后&#xff0c;打开环境变量&#xff0c;你会发现nvm为我们自动配置好…

vue对于安装依赖时不好习惯的反省

因为一个不好的习惯&#xff0c;我总是喜欢–save去安装依赖包&#xff0c;然后发现最后打包后的内容总是很大。就想着怎么能让包小一些&#xff0c;就发现我遗漏了vue安装依赖的一个小知识点 安装依赖的时候可以-s -d -g去安装&#xff0c;要根据使用的内容选择去安装&#xf…

MYSQL学习笔记:MYSQL存储引擎

MYSQL学习笔记&#xff1a;MYSQL存储引擎 MYSQL是插件式的存储引擎 存储引擎影响数据的存储方式 存储引擎是用来干什么的&#xff0c;innodb和myisam的主要区别–数据存储方式----索引 mysql> show engines; ----------------------------------------------------------…

Linux_动静态库

动态库 静态库 刚开始学编程时&#xff0c;需要下载一个环境&#xff08;vs2019&#xff09;&#xff0c;这个环境包括编译器和标准库&#xff0c;标准头文件。那么什么是库呢&#xff0c;库和头文件有什么关系呢&#xff1f; 头文件里面放的函数声明&#xff0c;库文件里面放…

python面向对象三大特性详解 - 封装 继承 多态

前言 面向对象编程有三大特性&#xff1a;封装、继承、多态&#xff0c;本文带大家来认识和了解这三个特性~ 补充 - 新式类 & 经典类 在python2.x中&#xff0c;新式类是继承了object类的子类&#xff0c;以及该子类的子类 子子类...&#xff1b;经典类就是没有继承没有…

新项目,从0到1,SpringBoot+Vue.js权限管理系统,拿去做毕设

大家好&#xff0c;我是 jonssonyan 最近把以前做的权限管理系统重新整理了一下&#xff08;将一些不规范的地方规范了一下&#xff0c;并且在关键地方写了注释&#xff09;&#xff0c;代码全部开源&#xff0c;这个项目是以现在主流的前后端分离模式开发的&#xff0c;包含前…

二叉树基础总结

目录 树的定义&#xff1a; 深度和高度&#xff1a; 二叉树 由来 二叉树种类&#xff1a; 满二叉树&#xff1a; 完全二叉树&#xff1a; 严格二叉树&#xff08;Strict Binary Tree&#xff09;&#xff1a; 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;&…

Android ·移动应用开发 创建第一个Android项目

文章目录 一、创建第一个Android项目1.1 准备好Android Studio1.2 运行程序1.3 程序结构是什么app下的结构res - 子目录&#xff08;所有图片、布局、字符串等资源&#xff09;AndroidManifest.xml 有四大组件&#xff0c;程序添加权限声明 Project下的结构 二、开发android时&…

顾问聘请协议(模板)

甲方&#xff1a;________________   乙方&#xff1a;________________ 诚信合作是一切事业发展的基础&#xff0c;外部智力是企业进步的源泉。甲、乙双方经友好协商达成本协议&#xff0c;甲方愿意聘请乙方为特邀管理顾问&#xff0c;乙方愿按本协议内容与甲方合作。 一、合…

算法学习——LeetCode力扣二叉树篇8

算法学习——LeetCode力扣二叉树篇8 669. 修剪二叉搜索树 669. 修剪二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high…

Git中Idea操作git及Git Flow

目录 一、Idea中使用Git 1.idea配置Git和Gitee 2.实践操作 1.将本地项目推送到远程 2.从远程库克隆项目到本地 二、Git Flow 1.什么是Git Flow 2.工作流程 3.实践操作 一、Idea中使用Git 1.idea配置Git和Gitee 第一步&#xff1a;设置git.exe的安装路径 在设置中的…

[计算机提升] 备份系统:设置备份

6.5 备份系统&#xff1a;设置备份 1、进入到控制面板系统和安全\备份和还原&#xff0c;点击右侧的设置备份&#xff1a; 2、在弹出的设置备份对话框中&#xff0c;选择要保存的位置&#xff0c;点击下一步开始备份。 3、选择要备份的内容。根据需要选择即可。这种备份的…

计算机毕业设计springboot_vue房屋租赁系统_ku668

1.掌握Html&#xff0c;Css&#xff0c;JavaScript等基础编程语言。 2.掌握Vue框架&#xff0c;node环境&#xff0c;数据库等知识。 3.掌握开发系统的基本流程。 …

【原创 附源码】Flutter集成Apple支付详细流程(附源码)

最近有时间&#xff0c;特意整理了一下之前使用过的Flutter平台的海外支付&#xff0c;附源码及demo可供参考 这篇文章只记录Apple支付的详细流程&#xff0c;其他相关Flutter文章链接如下&#xff1a; 【原创 附源码】Flutter集成谷歌支付详细流程(附源码) 【原创 附源码】F…

爬虫——ajax和selenuim总结

为什么要写这个博客呢&#xff0c;这个代码前面其实都有&#xff0c;就是结束了。明天搞个qq登录&#xff0c;这个就结束了。 当然也会更新小说爬取&#xff0c;和百度翻译&#xff0c;百度小姐姐的爬取&#xff0c;的对比爬取。总结嘛&#xff01;&#xff01;&#xff01;加…

VUE基础知识(JAVA后端入门篇)

VUE基础知识&#xff08;JAVA后端入门篇&#xff09; Vue是一套前端框架&#xff0c;免除原生JavaScriptr中的DOM操作&#xff0c;简化书写基于MVVM(Model–View-ViewModel)思想&#xff0c;实现数据的双向绑定&#xff0c;将编程的关注点放在数据上Vue.js - 渐进式 JavaScrip…

JSP知识点

1、JSP概述 1.1 什么是JSP html java代码 JSP动态标签 jsp JavaServer page 在静态页面上添加动态信息就可以了&#xff0c;如果是Servlet还需要一行一行的输出。 通常在前台开发人员给出静态页面后&#xff0c;后台开发人员只需在静态页面中添加动态信息即可&#xff…

算法学习——LeetCode力扣回溯篇3

算法学习——LeetCode力扣回溯篇3 491. 非递减子序列 491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。…