代码随想录算法训练营Day24 回溯算法| 理论基础 77. 组合
代码随想录算法训练营
回溯
什么是回溯法
回溯法也可以叫做回溯搜索法,它是一种搜索的方式。
在二叉树系列中,我们已经不止一次,提到了回溯,例如二叉树:以为使用了递归,其实还隐藏着回溯。
回溯是递归的副产品,只要有递归就会有回溯。
回溯法的效率
回溯法的性能如何呢,这里要和大家说清楚了,虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法。
因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。
那么既然回溯法并不高效为什么还要用它呢?
因为没得选,一些问题能暴力搜出来就不错了,撑死了再剪枝一下,还没有更高效的解法。
回溯法解决的问题
回溯法,一般可以解决如下几种问题:
- 组合问题:N个数里面按一定规则找出k个数的集合
- 切割问题:一个字符串按一定规则有几种切割方式
- 子集问题:一个N个数的集合里有多少符合条件的子集
- 排列问题:N个数按一定规则全排列,有几种排列方式
- 棋盘问题:N皇后,解数独等等
相信大家看着这些之后会发现,每个问题,都不简单!
另外,会有一些同学可能分不清什么是组合,什么是排列?
组合是不强调元素顺序的,排列是强调元素顺序。
例如:{1, 2} 和 {2, 1} 在组合上,就是一个集合,因为不强调顺序,而要是排列的话,{1, 2} 和 {2, 1} 就是两个集合了。
记住组合无序,排列有序,就可以了。
如何理解回溯法
回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!
因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度。
递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。
这块可能还不太理解,后面的回溯算法解决的所有题目中,都会强调这一点并画图举相应的例子,现在有一个印象就行。
回溯法模板
回溯三部曲
- 回溯函数模板返回值及参数
回溯算法中函数返回值一般为void。
伪代码:
void backtracking(参数)
- 回溯函数终止条件
回溯函数仍是树形结构,所以一定要有终止条件,满足条件时即可 - 回溯搜索的遍历过程
回溯法一般是集合中递归搜索,集合的大小构成了树的宽度。
回溯函数遍历过程伪代码为:
for(选择:本层集合中元素(书中节点孩子的数量就是集合的大小)){
处理节点;
backtracking(路径,选择列表);//递归
回溯,撤销处理结果
}
for循环就是遍历集合区间,可以理解一个节点有多少个孩子,for就执行多少次。
backtracking在这里自己调用自己,实现递归。
for循环可以理解是横向遍历,backtracking是纵向遍历。
框架如下:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77.组合
题目链接:77.组合
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
总体思路
最直接的想法是用两个for循环进行输出,但k的值就是循环的层数,当k越大时就越难进行使用,所以本题应该使用回溯。
回溯搜索法来了,虽然回溯法也是暴力,但至少能写出来,不像for循环嵌套k层让人绝望。(回溯也是暴力,只不过更文明)
那么回溯法怎么暴力搜呢?
上面我们说了要解决 n为100,k为50的情况,暴力写法需要嵌套50层for循环,那么回溯法就用递归来解决嵌套层数的问题。
递归来做层叠嵌套(可以理解是开k层for循环),每一次的递归中嵌套一个for循环,那么递归就可以用于解决多层嵌套循环的问题了。
此时递归的层数大家应该知道了,例如:n为100,k为50的情况下,就是递归50层。
本题可称为如下树状结构:
回溯三部曲
- 回溯函数返回值及参数
首先定义两个全局变量,一个用来储存符合条件的单一结果,另一个存放符合条件结果的集合
vector<vector<int>> result; // 存放符合条件结果的集合
vector<int> path; // 用来存放符合条件结果
函数里一定有两个参数,既然是集合n里面取k个数,那么n和k是两个int型的参数。
然后还需要一个参数,为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。
为什么要有这个startIndex呢?startIndex 就是防止出现重复的组合。
从下图中红线部分可以看出,在集合[1,2,3,4]取1之后,下一层递归,就要在[2,3,4]中取数了,那么下一层递归如何知道从[2,3,4]中取数呢,靠的就是startIndex。
所以需要startIndex来记录下一层递归,搜索的起始位置。
vector<vector<int>> result; // 存放符合条件结果的集合
vector<int> path; // 用来存放符合条件单一结果
void backtracking(int n, int k, int startIndex)
- 回溯函数的终止条件
什么时候到达所谓的叶子节点了呢?
path这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了,在图中path存的就是根节点到叶子节点的路径。
此时用result二维数组,把path保存起来,并终止本层递归。
所以终止条件代码如下:
if (path.size() == k) {
result.push_back(path);
return;
}
- 回溯函数的遍历过程
回溯法的搜索过程就是一个树型结构的遍历过程,在如下图中,可以看出for循环用来横向遍历,递归的过程是纵向遍历。
for循环每次从startIndex开始遍历,然后用path保存取到的节点i。
代码如下:
for (int i = startIndex; i <= n; i++) { // 控制树的横向遍历
path.push_back(i); // 处理节点
backtracking(n, k, i + 1); // 递归:控制树的纵向遍历,注意下一层搜索要从i+1开始
path.pop_back(); // 回溯,撤销处理的节点
}
可以看出backtracking(递归函数)通过不断调用自己一直往深处遍历,总会遇到叶子节点,遇到了叶子节点就要返回。
backtracking的下面部分就是回溯的操作了,撤销本次处理的结果。
最终代码实现:
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;
}
};
回溯模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
代码随想录算法训练营Day24 回溯算法| 理论基础 77. 组合的更多相关文章
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,循环控制及其优化
上两篇博客 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案 8皇后以及N皇后算法探究,回溯算法的JAVA实现,非递归,数据结构“栈”实现 研究了递归方法实现回溯,解决N皇后问题,下面我们来 ...
- Leetcode之回溯法专题-77. 组合(Combinations)
Leetcode之回溯法专题-77. 组合(Combinations) 给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输 ...
- 代码随想录算法训练营day24 | leetcode 77. 组合
基础知识 回溯法解决的问题都可以抽象为树形结构,集合的大小就构成了树的宽度,递归的深度构成的树的深度 void backtracking(参数) { if (终止条件) { 存放结果; return; ...
- 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案
八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例.该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同 ...
- 算法刷题--回溯算法与N皇后
所谓回溯算法,在笔者看来就是一种直接地思想----假设需要很多步操作才能求得最终的解,每一步操作又有很多种选择,那么我们就直接选择其中一种并依次深入下去.直到求得最终的结果,或是遇到明细的错误,回溯到 ...
- Java求解迷宫问题:栈与回溯算法
摘要: 使用栈的数据结构及相应的回溯算法实现迷宫创建及求解,带点JavaGUI 的基础知识. 难度: 中级 迷宫问题是栈的典型应用,栈通常也与回溯算法连用. 回溯算法的基本描述是: (1) 选择一个 ...
- LeetCode37 使用回溯算法实现解数独,详解剪枝优化
本文始发于个人公众号:TechFlow,原创不易,求个关注 数独是一个老少咸宜的益智游戏,一直有很多拥趸.但是有没有想过,数独游戏是怎么创造出来的呢?当然我们可以每一关都人工设置,但是显然这工作量非常 ...
- 代码随想录算法训练营day01 | leetcode 704/27
前言 考研结束半个月了,自己也简单休整了一波,估了一下分,应该能进复试,但还是感觉不够托底.不管怎样,要把代码能力和八股捡起来了,正好看到卡哥有这个算法训练营,遂果断参加,为机试和日后求职打下一个 ...
- 【算法训练营day4】LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表II
[算法训练营day4]LeetCode24. 两两交换链表中的结点 LeetCode19. 删除链表的倒数第N个结点 LeetCode面试题 02.07. 链表相交 LeetCode142. 环形链表 ...
- LeetCode通关:连刷十四题,回溯算法完全攻略
刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...
随机推荐
- Linux & 标准C语言学习 <DAY8_2>
一.函数 Function 一段具有某一项功能的代码集合,是C语言管理代码的最小单位 把代码封装成一个个函数,方便管理和调用函数 1.函数分类 标准库函数: ...
- mybatis-spring注解MapperScan的原理
很多开发者应该都知道,我们只使用@MapperScan这个注解就可以把我们写的Mybatis的Mapper接口加载到Spring的容器中,不需要对每个Mapper接口加@Mapper这个注解了,加快了 ...
- vue核心原理(Diff算法、虚拟dom)
核心原理&源码 Diff 算法 这里参考大佬文章:https://mp.weixin.qq.com/s/oAlVmZ4Hbt2VhOwFEkNEhw diff 算法的进化 关于 diff 算法 ...
- ChatGPT 设计游戏剧情 | 基于 AI 5 天创建一个农场游戏,完结篇!
欢迎使用 AI 进行游戏开发! 在本系列中,我们将使用 AI 工具在 5 天内创建一个功能完备的农场游戏.到本系列结束时,您将了解到如何将多种 AI 工具整合到游戏开发流程中.本文将向您展示如何将 A ...
- NX二次开发:保存时导出PDF并打开
该工程为在保存时执行开发的功能,函数入口点ufput.其他还有新建.打开.另存等都可以加入开发的操作,具体看UF_EXIT下的介绍. 用户出口是一个可选特性,允许你在NX中某些预定义的位置(或出口)自 ...
- 面试突击:MVCC 和间隙锁有什么区别?
MVCC 和间隙锁是两种完全不同的机制,但它们的目的都是相同的,都是用来保证数据库并发访问的,我们先来看二者的定义. MVCC 定义 MVCC 是多版本并发控制(Multi-Version Concu ...
- Comic Life - 超棒的漫画制作工具,拥有多种动画模版,创作属于自己的漫画
Comic Life是一个照片编辑器,能够添加各种效果,并基于它们创建漫画.该工具包包括各种各样的模板,可以很容易地将照片放置在工作表上,还有大量各种形状的标注.除了拼贴画上的标注之外,您还可以添加带 ...
- Prometheus服务发现之kubernetes_sd_config
一.为什么要使用Prometheus服务发现 之前我们讲过通过配置prometheus-operator的CRD ServiceMonitor来达到K8S集群相关组件和微服务的监控的目的,可以在Ser ...
- 阿里巴巴为什么这样强制从List中删除元素
还是先举个例子,你侄女对天文知识感兴趣,然后你就用程序写了太阳系九大星系(水星.金星.地球.火星.木星.土星.天王星.海王星.冥王星)的运行轨迹图,然后拿给侄女看.然后她说错了错了,你的知识太旧了,多 ...
- Etherscan本地多文件开源(VScode)
项目创建 创建文件夹 mkdir Duckereum cdDuckereum 添加nodejs配置 npm init -y 安装依赖添加 npm install -D hardhat npm ...