代码随想录Day22
77. 组合
给定两个整数 n 和 k,返回范围 [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
正解(回溯)
直接的解法当然是使用for循环,例如示例中k为2,很容易想到 用两个for循环,这样就可以输出 和示例中一样的结果。
输入:n = 100, k = 3 那么就三层for循环
如果n为100,k为50呢
此时就会发现虽然想暴力搜索,但是用for循环嵌套连暴力都写不出来
上面我们说了要解决 n为100,k为50的情况,暴力写法需要嵌套50层for循环,那么回溯法就用递归来解决嵌套层数的问题。
递归来做层叠嵌套(可以理解是开k层for循环),每一次的递归中嵌套一个for循环,那么递归就可以用于解决多层嵌套循环的问题了。
- 递归函数的返回值以及参数
在这里要定义两个全局变量,一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。
函数里一定有两个参数,既然是集合n里面取k个数,那么n和k是两个int型的参数。
然后还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。 - 回溯函数终止条件
什么时候到达所谓的叶子节点了呢?
path这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了,在图中path存的就是根节点到叶子节点的路径。 - 单层搜索的过程
回溯法的搜索过程就是一个树型结构的遍历过程,在如下图中,可以看出for循环用来横向遍历,递归的过程是纵向遍历。
for循环每次从startIndex开始遍历,然后用path保存取到的节点i。
上代码(●'◡'●)
class Solution {
private:
vector<vector<int>> result; // 存放符合条件结果的集合
vector<int> path; // 用来存放符合条件结果
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
result.push_back(path);
return;
}
for (int i = startIndex; i <= n; i++) {
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归
path.pop_back(); // 回溯,撤销处理的节点
}
}
public:
vector<vector<int>> combine(int n, int k) {
result.clear(); // 可以不写
path.clear(); // 可以不写
backtracking(n, k, 1);
return result;
}
};
组合问题是回溯法解决的经典问题,我们开始的时候给大家列举一个很形象的例子,就是n为100,k为50的话,直接想法就需要50层for循环。
从而引出了回溯法就是解决这种k层for循环嵌套的问题。
然后进一步把回溯法的搜索过程抽象为树形结构,可以直观的看出搜索的过程。
接着用回溯法三部曲,逐步分析了函数参数、终止条件和单层搜索的过程。
优化(剪枝)
来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了。

正如图所示,这种优化就像是把递归树上多余的枝杈剪掉了
图中每一个节点(图中为矩形),就代表本层的一个for循环,那么每一层的for循环从第二个数开始遍历的话,都没有意义,都是无效遍历。
所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。
如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
接下来看一下优化过程如下:
- 已经选择的元素个数:path.size();
- 还需要的元素个数为: k - path.size();
- 在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历
优化代码(●ˇ∀ˇ●)
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(int n, int k, int startIndex) {
if (path.size() == k) {
result.push_back(path);
return;
}
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方
path.push_back(i); // 处理节点
backtracking(n, k, i + 1);
path.pop_back(); // 回溯,撤销处理的节点
}
}
public:
vector<vector<int>> combine(int n, int k) {
backtracking(n, k, 1);
return result;
}
};
写博不易,请大佬点赞支持一下8~
代码随想录Day22的更多相关文章
- 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素
第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...
- 代码随想录第八天 |344.反转字符串 、541. 反转字符串II、剑指Offer 05.替换空格 、151.翻转字符串里的单词 、剑指Offer58-II.左旋转字符串
第一题344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 s 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这 ...
- 代码随想录-day1
链表 今天主要是把链表专题刷完了,链表专题的题目不是很难,基本都是考察对链表的操作的理解. 在处理链表问题的时候,我们通常会引入一个哨兵节点(dummy),dummy节点指向原链表的头结点.这样,当我 ...
- 代码随想录 day0 博客怎么写
前言 2.25日开始记录自己的博客生涯以及代码随想录训练营的每日内容 一.题目链接怎么找?怎么设置连接? 力扣题目链接1:力扣 二.正文怎么写? 二分查找 算法思路: 二分查找需要保证数组为有序数组同 ...
- 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)
背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...
- 代码随想录算法训练营day22 | leetcode 235. 二叉搜索树的最近公共祖先 ● 701.二叉搜索树中的插入操作 ● 450.删除二叉搜索树中的节点
LeetCode 235. 二叉搜索树的最近公共祖先 分析1.0 二叉搜索树根节点元素值大小介于子树之间,所以只要找到第一个介于他俩之间的节点就行 class Solution { public T ...
- 代码随想录第七天| 454.四数相加II、383. 赎金信 、15. 三数之和 、18. 四数之和
第一题454.四数相加II 给你四个整数数组 nums1.nums2.nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <= i, ...
- 代码随想录算法训练营day17 | leetcode ● 110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和
LeetCode 110.平衡二叉树 分析1.0 求左子树高度和右子树高度,若高度差>1,则返回false,所以我递归了两遍 class Solution { public boolean is ...
- 代码随想录算法训练营day13
基础知识 二叉树基础知识 二叉树多考察完全二叉树.满二叉树,可以分为链式存储和数组存储,父子兄弟访问方式也有所不同,遍历也分为了前中后序遍历和层次遍历 Java定义 public class Tree ...
- 代码随想录算法训练营day10 | leetcode 232.用栈实现队列 225. 用队列实现栈
基础知识 使用ArrayDeque 实现栈和队列 stack push pop peek isEmpty() size() queue offer poll peek isEmpty() size() ...
随机推荐
- LuBase 低代码开发框架介绍 - 可私有化部署
框架定位 面向开发人员,针对管理软件领域,对页面交互和通用功能进行高阶封装,逐步打造成平台型.生态型开发工具. 涓涓细流 ,汇聚成海,基于 PBC(组件式开发)开发理念,让功能模块的复用更简单. 让管 ...
- Zynq 7000的3种IO
概念 MIO MIO:多功能IO接口(分配在 GPIO 的 Bank0 和Bank1),属于Zynq的PS部分,在芯片外部有54个引脚.这些引脚可以用在GPIO.SPI.UART.TIMER.Ethe ...
- 在C#中使用RabbitMQ做个简单的发送邮件小项目
在C#中使用RabbitMQ做个简单的发送邮件小项目 前言 好久没有做项目了,这次做一个发送邮件的小项目.发邮件是一个比较耗时的操作,之前在我的个人博客里面回复评论和友链申请是会通过发送邮件来通知对方 ...
- 为什么说这款99元国产ARM工业平台,具有“划时代”意义
随着近年来进口处理器涨价不断,大家会发现市面上已很少见到99元的ARM工业核心板出售. 为满足客户对低成本.高性能的需求,创龙科技推出基于全志T113-i国产ARM工业处理器的"划时代&qu ...
- 写sql语句思路--28道关于教师、学生、成绩表的练习题---个人思路
针对学生教师的28道练习题的思路 链接:https://pan.baidu.com/s/1TgqFAe7i0PAkZOm47-Jd0A 提取码:vvi6 部分截图如下: -- sql 28道练习题答案 ...
- spring-关于组件的注入及获取流程
一.组件注入的基本流程: 容器初始化: Spring应用启动时,会读取配置(如XML配置.注解配置等),并根据这些配置创建Bean定义(BeanDefinition). 根据Bean定义,Spring ...
- TOPSIS模型原理以及代码实现
TOPSIS 法是一种常用的组内综合评价方法,能充分利用原始数据的信息,其结果能精确地反映各评价方案之间的差距.下面我们来介绍具体步骤与代码实现 目录 问题提出 第一步:数据输入 1.如何从excel ...
- 编程好帮手:通义灵码(TONGYI Lingma),是阿里云出品的一款基于通义大模型的智能编码辅助工具
通义灵码(TONGYI Lingma),是阿里云出品的一款基于通义大模型的智能编码辅助工具,提供行级/函数级实时续写.自然语言生成代码.单元测试生成.代码注释生成.代码解释.研发智能问答.异常报错排查 ...
- [oeasy]python0012_程序写错了怎么办
运行python文件_报错处理_NameError 回忆上次内容 回忆 上次内容 vi oeasy.py 用 vi 编辑 oeasy.py cat oeasy.py 用 cat 查看 oeasy. ...
- 入门Vue+.NET 8 Web Api记录(一)
做自己感觉有意思的或者能解决自己需求的项目作为入门,我觉得是有帮助的,不会觉得那么无聊. 一个最简单的前后端分离项目应该是怎么样的? 我觉得就是前端有个按钮,点击向后端发送一个get请求,获取到数据后 ...