代码随想录算法训练营第二十四天 | 回溯算法理论基础,77. 组合 [回溯篇]

代码随想录算法训练营第二十四天

  • 回溯算法理论基础
    • 什么是回溯法
    • 回溯法的理解
    • 回溯法模板
  • LeetCode 77.组合
    • 题目描述
    • 思路
    • 参考代码
    • 总结
    • 修改后的代码(微调整)
    • 优化版本
    • 优化后的参考代码

回溯算法理论基础

文章讲解:代码随想录#回溯算法理论基础
视频讲解:带你学透回溯算法(理论篇)| 回溯法精讲!

什么是回溯法

回溯法也叫做回溯搜索法,是一种搜索的方式。
回溯是递归的副产品,只要有递归就会有回溯。
回溯法的效率并不高,它的本质就是穷举法,有时候也会有剪枝的操作。

有些问题只有通过暴力穷举才能解决,比如可以解决以下问题:
在这里插入图片描述

回溯法的理解

回溯法解决的问题都可以抽象成一个树形结构。
回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树深度。
由于递归有终止条件,所以它是一棵高度有限的树。

回溯法模板

递归有三部曲,同理回溯也有三部曲。

  • 回溯函数体的返回值以及参数
    回溯算法中函数返回值一般为void。
    参数不能提前确定的,需要在根据处理逻辑来确定参数。
    所以回溯函数代码如下
void backtracking(参数)
  • 回溯函数终止条件
    一般情况下搜到叶子节点就找到了满足条件的一种解决方法,需要将这个方法保存起来,同时要结束本层递归。
if (终止条件) {存放结果;return;
}
  • 回溯搜索的遍历过程
    回溯一般都是在集合中递归搜索 ,集合的大小构成了树的宽度,递归的深度构成了树的深度。
for(选择:本层集合中元素(树中节点孩子的数量就是集合的大小)){处理节点;backtracking(路径,选择列表); // 继续递归回溯处理;
}

for循环就是遍历集合区间,可以理解一个节点有多少个孩子,这个for循环就会执行多少次。
其实,for循环就是横向遍历,递归就是纵向遍历。

回溯算法模板框架如下:

void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}

LeetCode 77.组合

题目链接:77.组合
文章讲解:代码随想录#77.组合
视频讲解:带你学透回溯算法-组合问题(对应力扣题目: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

思路

这是一道经典的回溯题,求的是组合,并非排列。
对于组合,【1,2】和【2,1】是一回事,对于排列【1,2】和【2,1】不相同。
组合是不强调元素顺序的,排列是强调元素顺序。
所以,这道题中某个元素进行过组合后,就需要不能再重复计算了。

那如何使用回溯算法呢?
上面说过回溯的问题都可以抽象成树形结构,盗图说明一下。
在这里插入图片描述
n相当于树的宽度,k相当于树的深度,每次搜索到叶子节点就表示找到了一个结果。

参考代码

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt = 0;void backtracking(int n, int k, int idx)
{if (result.index == k) { // 终止条件,当result中已经放入了k个元素时res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n; i++) { // 相当于树的横向遍历result.num[result.index++] = i; // 处理节点backtracking(n, k, i + 1); // 递归遍历下一层result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(10000 * sizeof(int*));backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要给returnColumnSizes分配内存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

总结

  1. 代码编译报这个错误,网上查到说明变量没有有效初始化,排查半天还是没有发现问题出在哪儿。
    在这里插入图片描述
  2. 原因终于找到了,在力扣上不能在函数外面初始化全局变量,否则就会出现各种异常现象
    在这里插入图片描述
    将如下修改,提交就AC了

修改后的代码(微调整)

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt;  // ※※※ 切记:千万不能在这块初始化全局变量!!!void backtracking(int n, int k, int idx)
{if (result.index == k) { // 终止条件,当result中已经放入了k个元素时res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n; i++) { // 相当于树的横向遍历result.num[result.index++] = i; // 处理节点backtracking(n, k, i + 1); // 递归遍历下一层result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(200000 * sizeof(int*));cnt = 0; // 初始化全局变量backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要给returnColumnSizes分配内存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

优化版本

对于部分回溯问题是可以通过减枝操作来优化效率的。
本题中可以进行减枝的地方就是循环遍历的条件,想想我们需要k个数,如果在遍历时n少于k时,那后面的循环遍历就没有必要了。
代码中的循环条件为: (int i = idx; i <= n; i++)

可以进行如下的优化:
①已经选择的元素个数为 result.index
②那还需要的元素个数为 k - result.index
③i为当前遍历的元素位置,n - i 表示剩余的元素个数
那就这样的关系: n - i + 1 >= k - result.index (剩余的元素个数要大于等于还需要的元素个数)
为什么要+1呢?因为是要包括当前的起始位置。

所以循环条件优化后为:for (int i = idx; i <= n - (k - result.index) + 1; i++)

优化后的参考代码

typedef struct {int index;int num[100];
}Result;Result result = {0};
int **res = NULL;
int cnt;void backtracking(int n, int k, int idx)
{if (result.index == k) { // 终止条件,当result中已经放入了k个元素时res[cnt] = (int*)malloc(k * sizeof(int));for(int i = 0; i < k; i++) {res[cnt][i] = result.num[i];}cnt++;return;}for (int i = idx; i <= n - (k - result.index) + 1; i++)  { // 相当于树的横向遍历result.num[result.index++] = i; // 处理节点backtracking(n, k, i + 1); // 递归遍历下一层result.index--; // 回溯result.num[result.index] = 0;}
}int** combine(int n, int k, int* returnSize, int** returnColumnSizes) {res = (int**)malloc(200000 * sizeof(int*));cnt = 0;backtracking(n, k, 1);*returnSize = cnt;*returnColumnSizes = (int*)malloc(sizeof(int) * cnt); // 需要给returnColumnSizes分配内存for (int i = 0; i < cnt; i++) {(*returnColumnSizes)[i] = k;}return res;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[WebDav] WebDav基础知识

文章目录 什么是WebDavWebDav常用命令WebDav常用命令的测试&#xff08;代码&#xff09;PROPFIND 方法测试PUT 方法测试GET 方法测试PROPPATCH方法 WebDav缓存Cache-ControlEtag测试 强制重新验证不需要缓存 WebDav的锁WebDav的状态码WebDav身份验证WebDav版本控制WebDav和FTP…

思考:如何写出让同事难以维护的代码?

本文从【程序命名&注释】【数据类型&类&对象】【控制执行流程】和【程序/结构设计】四个方面梳理了一些真实案例&#xff0c;相信通过这些案例你能迅速get技能&#xff1a;如何写出让同事难以维护的代码doge。 比起什么程序员删库跑路&#xff0c;我更喜欢「写出让…

高校学科竞赛平台|基于springboot高校学科竞赛平台设计与实现(源码+数据库+文档)

高校学科竞赛平台目录 目录 基于springboot高校学科竞赛平台设计与实现 一、前言 二、系统功能设计 三、系统实现 1、竞赛题库管理 2、竞赛信息管理 3、晋级名单管理 4、往年成绩管理 5、参赛申请管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最…

Flask框架:用Python打造精巧而强大的Web应用

在当今数字化时代&#xff0c;Web应用的需求不断增长&#xff0c;而对于开发者来说&#xff0c;选择一个适合的框架来构建Web应用是至关重要的。Flask框架作为一个简洁而灵活的Python微型框架&#xff0c;以其优雅的设计和丰富的可扩展性&#xff0c;为开发者提供了一个强大而精…

HAT论文详解:Activating More Pixels in Image Super-Resolution Transformer

code&#xff1a;https://github.com/XPixelGroup/HAT paper: https://arxiv.org/abs/2309.05239 1. 概述 本文是对Swinir的改进&#xff0c;目前很多图像超分Benchmark的SOTA。相对于SwinIR的改进主要有三个地方&#xff1a;1. 引入Channel Attention,以获得更好的全局能力&…

通过OCR实现纯数字识别

基于飞浆paddle训练框架 照这个改的 https://www.paddlepaddle.org.cn/documentation/docs/zh/practices/cv/image_ocr.html 训练不到10分钟 10epoch cpu&#xff1a;inter i5 8250 U 脚本生成的图10000 验证训练&#xff1a;3:7 预测结果 chatgpt写的代码&#xff0c;生成数…

PLC_博图系列☞基本指令“异或“运算

PLC_博图系列☞基本指令“异或“运算 文章目录 PLC_博图系列☞基本指令“异或“运算背景介绍X&#xff1a;“异或”运算说明参数示例真值表 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 异或 背景介绍 这是一篇关于PLC编程的文章&#xff0c;特别是关于西门子的…

shell脚本实现Mysql分库分表备份

一.数据库的分库分表&#xff1f; 12张图把分库分表讲的明明白白&#xff01;阿里面试&#xff1a;我们为什么要分库分表https://mp.weixin.qq.com/s?__bizMzU0OTE4MzYzMw&mid2247547792&idx2&sn91a10823ceab0cb9db26e22783343deb&chksmfbb1b26eccc63b784879…

PCIE1—快速实现PCIE接口上下位机通信(一)

1.简介 PCI Express&#xff08;PCIE&#xff09;是一种高速串行总线标准&#xff0c;广泛应用于计算机系统中&#xff0c;用于连接主板和外部设备。在FPGA领域中&#xff0c;PCIE也被广泛应用于实现高速数据传输和通信。FPGA是一种灵活可编程的集成电路&#xff0c;可以根据需…

猫头虎分享已解决Bug || Web服务故障:WebServiceUnavailable, HTTPServerError

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

ubuntu常见配置

ubuntu各个版本的安装过程大差小不差&#xff0c;可以参考&#xff0c;ubuntu20.04 其它版本换一下镜像版本即可 安装之后需要配置基本的环境&#xff0c;我的话大概就以下内容&#xff0c;后续可能有所删改 sudo apt-get update sudo apt-get install gcc sudo apt-get inst…

vulnhub靶场之Deathnote

一.环境搭建 1.靶场描述 Level - easy Description : dont waste too much time thinking outside the box . It is a Straight forward box . This works better with VirtualBox rather than VMware 2.靶场下载 https://www.vulnhub.com/entry/deathnote-1,739/ 3.启动环…

网络安全“降本增笑”的三大帮手

在网络安全这个快速变化和危机四伏的领域中&#xff0c;通过使用正确的工具和方法&#xff0c;我们可以在工作中取得更高的效率&#xff0c;并降低相关成本。 雷池社区版 雷池社区版—开源Web应用防火墙。这款产品凭借强大的规则引擎&#xff0c;它允许用户自定义安全策略&…

洛谷p1002过河卒

[NOIP2002 普及组] 过河卒 题目描述 棋盘上 A A A 点有一个过河卒&#xff0c;需要走到目标 B B B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马&#xff0c;该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为…

三防平板丨三防工业平板电脑丨三防平板电脑有哪些优势?

三防平板电脑通常使用特殊材料和制造工艺来达到防水、防尘、防摔的目的&#xff0c;这样可以在极端条件下使用&#xff0c;并保证设备的稳定性和可靠性。因此&#xff0c;三防平板电脑适用于各种恶劣环境&#xff0c;如户外野营、物流、工业制造等应用场景。那么相比于普通消费…

Pregnostic®–PE IIp ELISA,用于测量人源ESM-1水平

Pregnostic PE Pregnostic是由IQ Products公司开发的重点关注女性健康的产品线。其中的Pregnostic -PE项目&#xff0c;旨在开发有助于筛查孕期先兆子痫风险的产品&#xff0c;可用于区分早发性和晚发性先兆子痫。 ESM-1 内皮细胞特异性分子&#xff08;ESM-1&#xff09;&am…

BabylonJS 6.0文档 Deep Dive 动画(一):动画介绍

1. 动画介绍 无论动画如何实现&#xff0c;它都必须考虑所需的动作、时间、产生所需流动性所需的帧数以及序列中的关键点。这个介绍应该有助于理解Babylon.js是如何进行动画的&#xff0c;以及它们是如何实现的。 动画由一系列图像、帧生成&#xff0c;这些图像、帧一个接一个地…

【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《Spring 狂野之旅&#xff1a;底层原理高级进阶》 &#x1f680…

QT_day2

1.思维导图 2.使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff…

幻兽帕鲁1.4.1单机畅玩教程

文章目录 游戏简介游戏背景游戏玩法资源安装和配置教程游戏包的下载和安装Steam客户端下载和配置 毕业存档使用教程第一步&#xff1a;个人存档备份第二步&#xff1a;毕业存档导入 游戏简介 Palworld&#xff1a;一款由日本独立游戏工作室Pocketpair制作发行的动作角色扮演游…