组合问题

力扣题目链接(opens new window)

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

示例:

输入: n = 4, k = 2

输出:

[

[2,4],

[3,4],

[2,3],

[1,2],

[1,3],

[1,4],

]

思路

如果像题目给的例子那样,k是一个较小的值(比如2),那么一个很直接的想法是使用两层for循环去遍历数组即可

vector<vector<int>> res;
vector<int> temp;
for(int i = 0; i < n; ++i){
for(int j = i; j < n; ++j){
temp.push_back(i);
temp.push_back(j);
res.push_back(temp);
temp.clear();
}
}
return res;

那如果k要是100呢?写100层for循环?

显然,使用for循环进行暴力遍历的话,遍历的深度是有限的

既然涉及到了深度,那么递归树结构就很适合来解决此类问题了

首先,递归可以在设置的条件范围内搜索到足够的深度,我们实际上只用在递归的最后一层进行横向遍历(即for循环),其余的则在回溯过程中处理

其次,我们可以将整个搜索过程想象成一个树结构

当我们通过递归到达某个分支的"叶子结点"后,这条路径上保存的节点值就是一个组合的结果

下面贴一下卡哥的图和我经过理解补充的图,可以对比来看方便理解


可以看到,每层递归中都有一个for循环,目的是用于处理本层递归的横向遍历过程

当第一层递归被调用时,其中的for循环会依次处理[1,2,3,4],取1(将1保存)

此时又触发一层递归,第二层递归会依次处理[2,3,4],取2(将2保存)

此时,再次触发递归,到达第三层,在第三层递归中,我们判定用于保存结果的数组(目前为[1,2])的大小已经等于k值

因此,在第三层递归中触发了终止条件,递归结束

回溯,到达第二层递归,此时将结果数组中的2弹出

回溯,到达第一层递归,此时将结果数组中的1弹出

第一层递归中的for继续遍历到下一个数(即2),然后重复上述递归过程

当然,在第二层递归中,每次起始的遍历位置是不同的,后面代码里会解释

代码分析

写递归嘛,那还是遵循递归的步骤,但这里其实可以改叫回溯三部曲了

1、确定递归函数的返回值和参数

本质上我们是使用递归去操作/遍历一个数组,因此不需要返回值

输入参数那肯定是遍历区间n组合大小k

经过上面的分析得知,我们还需要一个数组来保存每层递归取到的结果

这里将该数组命名为path其记录的是我们从第一层递归到最里层递归这条路径上遇到并保存的值

还需要一个数组res来保存所有的path

并且,在第二层递归中,为了防止组合中出现重复值([1,2]和[2,1]被视为相同结果),我们需要不断调整遍历的范围

因此输入参数中还需要一个变量给出遍历的起始位置

class Solution {
public:
//定义数组
vector<int> path;
vector<vector<int>> res;//保存最终结果
void backtracking(int n, int k, int beginIndex){ } vector<vector<int>> combine(int n, int k) { }
};

2、确定终止条件

还是通过前面的讨论得知,当我们到达最里层递归时,需要终止。即到达我们想象的二叉树结果的叶子节点

如何判断"到达叶子节点"这件事呢?

其实可以通过路径数组path的大小来判断,如果path的大小等于k,说明递归的深度已经到头了,此时应该结束递归

class Solution {
public:
//定义数组
vector<int> path;
vector<vector<int>> res;//保存最终结果
void backtracking(int n, int k, int beginIndex){
//确定终止条件
if(path.size() == k){
//保存path到res
res.push_back(path);
return;//结束递归
}
} vector<vector<int>> combine(int n, int k) { }
};

3、确定单层处理逻辑

这一部分需要去规定在本层递归中应该如何遍历数组

还是结合前面的分析:我们在一层递归中实际需要做的是使用for对当前以beginIndex为起始,以n为结束(即[beginIndex, n])的数组进行遍历

那就写for循环呗

class Solution {
public:
//定义数组
vector<int> path;
vector<vector<int>> res;//保存最终结果
void backtracking(int n, int k, int beginIndex){
//确定终止条件
if(path.size() == k){
//保存path到res
res.push_back(path);
return;//结束递归
} //确定单层处理逻辑
//遍历本层递归的数组,范围是[beginIndex, n]
for(int i = beginIndex; i < n; ++i){
//添加当前路径节点值到path
path.push_back(i);
//进入下一层递归
backtracking(int n, int k, i + 1);//跳过当前值
//编写回溯处理逻辑
//回溯时删掉当前层保存的值
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) { }
};

打完收工

完整代码

在主函数中,把backtracking调用并给出beginIndex的初值即可

class Solution {
public:
//定义数组
vector<int> path;
vector<vector<int>> res;//保存最终结果
void backtracking(int n, int k, int beginIndex){
//确定终止条件
if(path.size() == k){
//保存path到res
res.push_back(path);
return;//结束递归
} //确定单层处理逻辑
//遍历本层递归的数组,范围是[beginIndex, n]
for(int i = beginIndex; i <= n; ++i){
//添加当前路径节点值到path
path.push_back(i);
//进入下一层递归
backtracking(n, k, i + 1);//跳过当前值
//编写回溯处理逻辑
//回溯时删掉当前层保存的值
path.pop_back();
}
}
vector<vector<int>> combine(int n, int k) {
//可以先清一下两个结果数组
//res.clear();
//path.clear();
backtracking(n, k, 1);//调用递归去操作数组
return res;
}
};
注意点

遍历本层递归时,i是要 小于等于 n的,并且beginIndex的初始值应该是1

这里需要厘清一个关系

本题中并不是真的有一个数组等着我们去遍历(分析时写[1,2,3,4]也只是便于理解),只是给定的一个范围,在该范围中找出所有两个元素的组合

因此要在[1,4]范围中找出所有两个元素的组合,就真的要从1开始,并且也要包含4

(不要错误的认为从1开始就漏了[1,2,3,4]中的1,结束位置是4就超出数组范围。因为根本就没有数组)

剪枝优化

TBD

【LeetCode回溯算法#01】图解组合问题的更多相关文章

  1. 【LeetCode回溯算法#07】子集问题I+II,巩固解题模板并详解回溯算法中的去重问题

    子集 力扣题目链接 给你一个整数数组 nums ,数组中的元素 互不相同 .返回该数组所有可能的子集(幂集). 解集 不能 包含重复的子集.你可以按 任意顺序 返回解集. 示例 1: 输入:nums ...

  2. 【LeetCode回溯算法#10】图解N皇后问题(即回溯算法在二维数组中的应用)

    N皇后 力扣题目链接(opens new window) n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击. 给你一个整数 n ,返回所有不同的 n 皇 ...

  3. 【LeetCode回溯算法#extra01】集合划分问题【火柴拼正方形、划分k个相等子集、公平发饼干】

    火柴拼正方形 https://leetcode.cn/problems/matchsticks-to-square/ 你将得到一个整数数组 matchsticks ,其中 matchsticks[i] ...

  4. leetcode回溯算法--基础难度

    都是直接dfs,算是巩固一下 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合. 给出数字到字母的映射如下(与电话按键相同).注意 1 不对应任何字母. 思路 一直 ...

  5. 【LeetCode回溯算法#06】复原IP地址详解(练习如何处理边界条件,判断IP合法性)

    复原IP地址 力扣题目链接(opens new window) 给定一个只包含数字的字符串,复原它并返回所有可能的 IP 地址格式. 有效的 IP 地址 正好由四个整数(每个整数位于 0 到 255 ...

  6. 【LeetCode回溯算法#08】递增子序列,巩固回溯算法中的去重问题

    递增子序列 力扣题目链接(opens new window) 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2. 示例 1: 输入:nums = [4,6,7,7] ...

  7. LeetCode初级算法--链表01:反转链表

    LeetCode初级算法--链表01:反转链表 搜索微信公众号:'AI-ming3526'或者'计算机视觉这件小事' 获取更多算法.机器学习干货 csdn:https://blog.csdn.net/ ...

  8. LeetCode刷题191203 --回溯算法

    虽然不是每天都刷,但还是不想改标题,(手动狗头 题目及解法来自于力扣(LeetCode),传送门. 算法(78): 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明: ...

  9. LeetCode通关:连刷十四题,回溯算法完全攻略

    刷题路线:https://github.com/youngyangyang04/leetcode-master 大家好,我是被算法题虐到泪流满面的老三,只能靠发发文章给自己打气! 这一节,我们来看看回 ...

  10. LeetCode:回溯算法

    回溯算法 这部分主要是学习了 labuladong 公众号中对于回溯算法的讲解 刷了些 leetcode 题,在此做一些记录,不然没几天就忘光光了 总结 概述 回溯是 DFS 中的一种技巧.回溯法采用 ...

随机推荐

  1. 【VictoriaMetrics】一个小优化:循环改查表,性能提升56.48 倍

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 做了一个 vm-storage 数据文件 merge 的工 ...

  2. 【JS 逆向百例】网洛者反爬练习平台第二题:JJEncode 加密

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  3. 大数据面试题集锦-Hadoop面试题(三)-MapReduce

    你准备好面试了吗?这里有一些面试中可能会问到的问题以及相对应的答案.如果你需要更多的面试经验和面试题,关注一下"张飞的猪大数据分享"吧,公众号会不定时的分享相关的知识和资料. 目录 ...

  4. c++基础之字符串、向量和数组

    上一次整理完了<c++ primer>的第二章的内容.这次整理本书的第3章内容. 这里还是声明一下,我整理的主要是自己不知道的或者需要注意的内容,以我本人的主观意志为准,并不具备普适性. ...

  5. 释放搜索潜力:基于ES(ElasticSearch)打造高效的语义搜索系统,让信息尽在掌握

    释放搜索潜力:基于ES(ElasticSearch)打造高效的语义搜索系统,让信息尽在掌握[1.安装部署篇--简洁版],支持Linux/Windows部署安装 效果展示 PaddleNLP Pipel ...

  6. 在数据增强、蒸馏剪枝下ERNIE3.0分类模型性能提升

    在数据增强.蒸馏剪枝下ERNIE3.0模型性能提升 项目链接: https://aistudio.baidu.com/aistudio/projectdetail/4436131?contributi ...

  7. HTTP请求头引发的注入问题 (SQL注入)

    关于请求头中注入问题的演示,这里我写了一些测试案例,用来测试请求头中存在的问题.我们常见的会发生注入的点有 Referer.X-Forwarded-For.Cookie.X-Real-IP.Accep ...

  8. blazor maui hybrid app显示本地图片

    啊... ... 一通操作下来感觉就是两个字 折磨 跨平台有跨平台的好处 但框架本身支持的有限 很多东西做起来很曲折 哎 这里总结一下笔者为了折腾本地图片显示的尝试 为什么要做本地图片展示呢 如果是做 ...

  9. C#/.NET/.NET Core优秀项目和框架2024年1月简报

    前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍.功能特点.使用方式以及部分功能截图 ...

  10. 《ASP.NET Core 与 RESTful API 开发实战》-- (第7章)-- 读书笔记(下)

    第 7 章 高级主题 7.4 HATEOAS 全称 Hypermedia AS The Engine Of Application State,即超媒体作为应用程序状态引擎.它作为 REST 统一界面 ...