【C/C++算法】蓝桥杯之递归算法(如何编写想出递归写法)

绪论:冲击蓝桥杯一起加油!!
在这里插入图片描述
每日激励:“不设限和自我肯定的心态:I can do all things。 — Stephen Curry”

绪论​:
————————
早关注不迷路,话不多说安全带系好,发车啦(建议电脑观看)。


递归

1. 什么是递归

简单来说:就是函数自己调用自己

2. 为什么会用到递归

常见的递归有:二叉树的遍历、快排、归并
在这里插入图片描述

其中递归的本质:

  1. 解决主问题,衍生相同的子问题
  2. 在处理子问题的时候,又出现了相同的子问题
  3. 所以本质就是不断自己调用自己,通过缩小问题最终解决最小子问题

3. 如何理解递归(非常重要!)

对于理解递归,可以从下面三个方向理解:

  1. 递归展开的细节图
  2. 二叉树中的题目
  3. 宏观看待递归的过程
    1. 不要在意递归的展开图
    2. 把递归的函数当成一个黑盒(具体里面如何操作关心,给他数据,返回结果)
    3. 相信这个黑盒一定能完成这个任务

(这个后面慢慢的到来,请继续往后看)

4. 如何写好一个递归?

  1. 先找到相同的子问题
    1. 根据子问题:决定了函数头的设计
    2. 本质也就是分析题目,然后得出解决子问题可能需要用到的参数(一般来说可以先粗力度的得到一个函数头,然后再在编写代码中不断弥补)
      在这里插入图片描述
  2. 只关心某个子问题是如何解决的
    1. 把他看成一个黑盒,并相信他能够替你完成任务
    2. 也就决定了函数体的书写,我们仅仅去想某个子问题的解决方法
    3. 因为这样再次调用该函数的时候,虽然条件参数可能不同,但子问题的解决方法是一致的
      在这里插入图片描述
  3. 最后再注意一下递归函数的出口即可
    1. 也就是防止无限递归的情况

总结:解决简单递归的三步:

非常重要,不过通过大量练习相信你就能很好的理解(可能初次看会迷糊~)

  1. 通过题目写出dfs的函数头
  2. 根据子问题写出函数内部逻辑
  3. 注意一下递归出口

深度优先遍历 vs 宽度优先遍历 vs 暴搜

  1. 深度优先遍历(搜索)dfs(depth):

    1. 通俗的来说就是:一条路走到黑
      在这里插入图片描述
  2. 宽度优先遍历(搜索)bfs(Breadth):

    1. 本质也就是:一层一层的遍历树
      在这里插入图片描述
  3. 搜索(暴搜)

    1. 本质就是暴力枚举一遍所有的情况,或者说就是将树中的所有节点都进行一次遍历
  4. 搜索问题的拓展:

拿全排列问题举例:
全排列是将一组数中的所有情况排出来(如下图)
在这里插入图片描述
那么可以使用树状图(决策树)的方式解决具体如下图
在这里插入图片描述
此时画出来后,我们需要的答案最终就能通过递归搜索的方式获取到:
在这里插入图片描述
所以说我们不能对于dfs、bfs来说局限于二叉树,而是当我们能画出类似的决策树的形式画出来那么就能使用dfs/bfs

回溯与剪枝(非常重要!)

  1. 回溯的本质:其实就是深搜中当我们尝试某种情况时,发现这种情况行不通,退回到上一级的操作就是回溯
    (如下图红线)
    在这里插入图片描述
  2. 剪枝来说:在我们回溯过程后可能遇到两种可以走的情况,而其中一种情况已经走过了知道行不通,那么这条路就代表被剪枝了
    (具体如下图)
    在这里插入图片描述

下面将通过5道题目带你理解递归,其中注意理解递归的三步(函数头,函数体,递归出口)


具体训练:

1. 汉诺塔

题目:

在这里插入图片描述

分析题目并提出,解决方法:

在这里插入图片描述
在这里插入图片描述
在过程中不允许大盘子摞在小盘子上面!

题解核心逻辑:

汉诺塔问题,可以用递归解决
如何来解决汉诺塔问题?
在这里插入图片描述

  1. 在N >= 2 时,我们要考虑先将最大的盘子放到 C柱上

问题就转移成了:

  1. 先把最大盘子上面的所有先放在B柱上,然后在移动 最大的盘子 到C柱
    在这里插入图片描述
  2. 第三步就是将所有B柱上的盘子在移动到C柱上
    在这里插入图片描述
  3. 再次理解:当N = 3时,看最上面的两个盘子,它的本质其实就是N = 2的情况,只不过这次是移动到B柱
    在这里插入图片描述
  4. 这里注意理解的是:操作1和操作3的本质是一样的,只不过借助的柱子不一样!
  5. 那么 N = … 他们的本质都是一样了,他们都是:
    1. 执行步骤1,将最大盘子上面的小盘子全部转移到B
    2. 执行步骤2,将最大盘子全部转移到C
    3. 执行步骤3,再将B上的转移到C即可完成
    4. 所以为什么可以用递归:解决大问题的时候,出现了相同的子问题,解决子问题的时候又出现了相同的子问题
      在这里插入图片描述

如何写代码:

  1. 挖掘重复子问题(函数头)
    1. 也就是上面的 步骤 1、 2、3所需要的
    2. 该题本质是:将 x 柱上的一堆盘子,借助 y 柱子,转移到 z 柱子上
    3. 那么函数头也就如下图
      在这里插入图片描述
  2. 只关心某一个子问题在做什么(函数体)
    1. 宏观的分析三个步骤的具体:
      在这里插入图片描述
  3. 函数出口
    1. 不难发现就是 N = 1 的时候是不同的,直接将A柱的盘子直接放到C柱上
    2. 那么当N=1时,将A柱的盘子直接放到C柱上,然后就可以退出了
      在这里插入图片描述
      具体步骤代码如下:
class Solution {
public:
//1. 挖掘重复子问题(得出函数头)
//将n个盘子移动到 借助柱子移动到目标柱子void h(vector<int>& A, vector<int>& B, vector<int>& C,int n){//出口:if(n == 1){// 将 A 上的移动到 C上C.push_back(A.back());A.pop_back();return ;}//2. 分析子问题:(得到函数体所需要的操作,并且相信他能完成)
//先将 A 柱上的 n-1 个移动到 B盘h(A,C,B,n-1);
//将 A 中的最后一个盘子移动到 C上C.push_back(A.back());A.pop_back();
//在将B上的盘子借助A全部移动到C上h(B,A,C,n-1);} void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {h(A,B,C,A.size());}
};

2. 合并两个有序链表

题目:

在这里插入图片描述

分析题目并提出,解决方法:

题目很好理解:就是拼接两个链表,过程中不允许创建空间

分析本题查看是否有重复子问题:
在这里插入图片描述
在这里插入图片描述

  1. 重复子问题(函数头的设计)

    1. 合并两个有序链表
    2. 那么也就仅需要两个 链表
      在这里插入图片描述
  2. 只关心某个子问题(函数体的设计)
    在这里插入图片描述

    1. 比大小(两个链表进行比较)得到较小的结点
    2. 将较小的结点看做合并后的链表头结点(通过修改该结点的next指针完成)
      在这里插入图片描述
    3. 最终返回 较小的结点
  3. 递归出口

    1. 那个指针先为空返回另外一个指针

总结:递归 = 重复子问题 + 宏观看待递归问题

题解核心逻辑:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:
//分析函数的目的:合并两个链表:所以函数头就是 两个链表即可ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
//编写出口:判断那个链表先为空,返回另外一个if(list1 == nullptr) return list2;if(list2 == nullptr) return list1;//编写子问题的具体操作:完成函数体//1. 找到较小的if(list1->val <= list2->val){list1->next =  mergeTwoLists(list1->next,list2);return list1; }else{list2->next =  mergeTwoLists(list1,list2->next);return list2; }}
};

小总结:

递归 VS 深搜

递归的展开图,其实就是对一棵树做一次深度优先遍历(dfs)
而在递归的过程中需要一个 进行保存,历史数据
在这里插入图片描述

循环(迭代) vs 递归

它们是能相互转换的,那么什么时候,用哪一个呢?
在这里插入图片描述
通过上面的分析和上图理解到:

  1. 当我们的一个遍历过程需要用到类似栈的东西进行保存数据时,就是比较麻烦的情况了,此时我们使用递归的形式就能很简单方便的写出程序
  2. 而当一些遍历过程比较简单,如上图右边结构,此时遍历仅仅只需要单方向的那么,此时就没必要使用递归,因为一个简单的遍历循环即可完成
    在这里插入图片描述

3. 反转链表

题目:

在这里插入图片描述

分析题目并提出,解决方法:

宏观角度看待问题:

  1. 让当前结点后面的链表先逆序,并且把头结点返回
    在这里插入图片描述
  2. 让当前结点添加到逆置后的链表在这里插入图片描述

第二个视角:将链表看成一颗树:

  1. 不断深度遍历到最后一个结点
  2. 返回最后一个结点
  3. 到达倒数第二层:执行将自己下一个结点的next置为自己,然后自己置为null(这里置为是为了保持所以子操作一致)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

题解核心逻辑:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {//使用递归的方式//宏观的看待问题://翻转链表(所以函数头就只需要一个链表即可,还需要返回新头结点)//子问题(将自己的next的next置为自己,再将自己置为空,并且还需要将最后的头结点返回回来)//出口:(当head为空时退出,表示遍历到了最后结点)//head == nullptr 时为了防止没有结点的情况if(head == nullptr || head->next == nullptr){return head;//返回结点}ListNode* newhaed = reverseList(head->next);head->next->next = head;head->next = nullptr;return newhaed;}
};

4. 两两交换链表中的节点

题目:

在这里插入图片描述

分析题目并提出,解决方法:

递归思想(宏观看待):

  1. 分析题目得到递归思想:将给到的链表中的两两进行交换顺序,那么也就仅需要一个链表参数即可
    在这里插入图片描述
  2. 看待某个子问题:
    1. dfs会返回一个新的链表的结点,使用tmp记录
    2. 将当前head的next的next指向haed,再将head的next指向tmp(完成交换的目的)
      在这里插入图片描述
  3. 退出情况,当head为空是退出
    1. 注意其中是以两个结点看成一起的,所以退出条件是:
    2. 当 head为空 || head->next 为空

题解核心逻辑:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     ListNode *next;*     ListNode() : val(0), next(nullptr) {}*     ListNode(int x) : val(x), next(nullptr) {}*     ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* swapPairs(ListNode* head) {//退出条件:if(head == nullptr || head->next == nullptr){return head;//返回当前结点}//子问题://1. 获取dfs后面的头结点ListNode* tmp = swapPairs(head->next->next);ListNode*  ret = head->next;ret->next = head;head->next = tmp;return ret;}
};

5. Pow(x, n)

题目:

在这里插入图片描述

分析题目并提出,解决方法:

很好理解 就是 求x n等于多少
解法1:暴力循环,让 x 乘 n 次即可(会超时)

解法二:

快速幂

快速幂的实现:

  1. 递归实现
  2. 循环实现

本题将递归实现:
例子:当我们要求 316

  • 就是将 316 不断的对半看(具体如下图)
    先求出 38 这样 38 * 38 = 316 同理 38 = 34 * 34 。。。
    在这里插入图片描述
  • 其中暴力解法要求 16 次,而使用这种方法只用求logn次
  • 附:当 n 为奇数时:多乘一个自身即可
    在这里插入图片描述
  1. 相同子问题 -->函数头
    • 求一个 x的n次幂 ==> pow(x,n)
  2. 只关心每个子问题做了什么 – > 函数体
    • 其中需要 判断 n 的奇偶性
    • 通过递归获取自身的值,并且判断奇偶性得出是否要多乘1位
  3. 递归出口
    • 当 n == 0 时返回1(这样上层 n = 1 处等于 1 * x(1 * 1 * x))
      在这里插入图片描述
      特殊情况:
  • n 为负数:
    在这里插入图片描述
  • n 可能是 -231 当变成整数就可能越界,所以得使用long long(整形的范围是 -231 ~ 231 - 1
    在这里插入图片描述

题解核心逻辑:

  • 其中注意 -(long long)n 这里的操作:它是将 n 的类型转换成了long long(并且不能写在-前面,只能挨着n
  • 只有这样当 n = -231 时强转为正数后就不会溢出
class Solution {
public:double myPow(double x, int n) {//递归实现://将 x^n 看成 x^(n/2) * x^(n/2) ...return n >= 0 ? pow(x,n) : 1/pow(x,-(long long)n);}double pow(double x,long long n){if(n == 0) return 1;double tmp= myPow(x,n/2);return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;}
};

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

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

相关文章

[ctfshow web入门] web5

前置知识 引用博客&#xff1a;phps的利用 当服务器配置了 .phps 文件类型时&#xff0c;访问 .phps 文件会以语法高亮的形式直接显示 PHP 源代码&#xff0c;而不是执行它。.phps被作为辅助开发者的一种功能&#xff0c;开发者可以通过网站上访问xxx.phps直接获取高亮源代码 …

day 8 TIM定时器

一、STM32 定时器概述 1. 定时器的概述定时器的基本功能&#xff0c;但是 STM32 的定时器除了具有定时功能之外&#xff0c;也具有定时器中断功能&#xff0c;还具有输入捕获&#xff08;检测外部信号&#xff09;以及输出比较功能&#xff08;输出不同的脉冲&#xff09;&…

Spring Boot 中使用 Redis:从入门到实战

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

hi3516cv610通过menuconfig关闭的宏记录

hi3516cv610通过menuconfig关闭的宏记录 defconfig为 hi3516cv610_debug_defconfig或hi3516cv610_new_defconfig 1、 变为 2、 变为 3、 变为 4、 变为 5、 变为

WebSocket 详解:构建一个复杂的实时聊天应用

文章目录 一、前言二、WebSocket 基础2.1 WebSocket 与 HTTP 的区别2.2 WebSocket 的优点 三、搭建 WebSocket 服务端3.1 安装 ws 和 redis 库3.2 创建 WebSocket 服务端3.3 创建用户身份验证 四、前端实现 WebSocket 客户端4.1 创建 Vue 3 项目4.2 实现 WebSocket 连接和用户注…

【JavaEE进阶】Spring AOP入门

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗 如有错误&#xff0c;欢迎指出~ AOP是Spring框架的第⼆⼤核⼼(第⼀⼤核⼼是 IoC) 什么是AOP&#xff1f; • AspectOrientedProgramming&#xff08;⾯向切⾯编程&#xff09; 什么是⾯向切⾯编程呢? 切…

算法思想之双指针(一)

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;算法思想之双指针(一) 发布时间&#xff1a;2025.4.4 隶属专栏&#xff1a;算法 目录 双指针算法介绍对撞指针&#xff1a;快慢指针&#xff1a; 例题移动零题目链接题目描述算法思路代码实现 复写零题目链接题目描…

【11408学习记录】英语写作黄金模板+语法全解:用FTC数据泄漏案掌握书信结构与长难句拆解(附思维导图)

2025.04.04 英语写作书信写作第一段私人信件公务信函 语法总结——简单句简单句的核心&#xff1a;谓语动词的变化词性的拓展限定词 形容词与副词介词短语 成分的扩展同位语插入语 非谓语动词 每日一句词汇 第一步&#xff1a;辨别第二步&#xff1a;断开第三步&#xff1a;简化…

手机显示5GA图标的条件

最近有星友问在什么情况下才能显示5G-A&#xff1f;虽然这个我也不知道&#xff0c;但是我有几个运营商的5G终端白皮书&#xff0c;从上面就可以找到答案。 如上是几个运营商显示5G-A的条件&#xff0c;基本上考虑的都是3CC的情况&#xff0c;联通还有考虑200M CA 2CC的场景&am…

网络:华为数通HCIA学习:IP路由基础

华为HCIA学习 IP路由基础路由协议或路由种类以及对应路由的优先级按工作区域分类&#xff1a;按工作机制及算法分类&#xff1a;路由的优先级路由器选择最优路由的顺序是什么? 前言自治系统LAN和广播域路由选路IP路由表路由度量建立路由表最长匹配原则路由器转发数据包总结 IP…

Docker 镜像相关的基本操作

一、Docker 镜像基本操作 1. 查找镜像 命令&#xff1a; docker search <镜像名称> 示例&#xff1a;查找 CentOS 镜像&#xff1a; docker search centos 命令解释&#xff1a; 默认从 Docker Hub 官方仓库上搜索镜像。搜索结果包含多个列&#xff1a; NAME&…

Linux文件特殊权限管理及进程和线程

acl 权限优先级 拥有者 > 特殊指定用户 > 权限多的组 >权限少的组 > 其他 mask阈值 mask是能够赋予指定用户权限的最大阀值 当设定完毕文件的acl列表之后用chmod缩小了文件拥有组的权力 mask会发生变化 恢复&#xff1a; setfacl -m m: 权限 :rwx 文件/…

NVIDIA AgentIQ 详细介绍

NVIDIA AgentIQ 详细介绍 1. 引言 NVIDIA AgentIQ 是一个灵活的库&#xff0c;旨在将企业代理&#xff08;无论使用何种框架&#xff09;与各种数据源和工具无缝集成。通过将代理、工具和代理工作流视为简单的函数调用&#xff0c;AgentIQ 实现了真正的可组合性&#xff1a;一…

算法设计与分析5(动态规划)

动态规划的基本思想 将一个问题划分为多个不独立的子问题&#xff0c;这些子问题在求解过程中可能会有些数据进行了重复计算。我们可以把计算过的数据保存起来&#xff0c;当下次遇到同样的数据计算时&#xff0c;就可以查表直接得到答案&#xff0c;而不是再次计算 动态规划…

怎么理解量子比特模型,迁移到量子计算机开始编程

怎么理解量子比特模型&#xff0c;迁移到量子计算机开始编程 视频链接&#xff1a; 好的现在是2025年的3月最后一天,3月31号,今天我们讨论的话题是量子编程,也就是在量子计算机上,使用特定的语言进行软件开发。当然我们要讨论的,不是,量子编程的某一门语言的技术细节,而是考虑…

使用Expo框架开发APP——详细教程

在移动应用开发日益普及的今天&#xff0c;跨平台开发工具越来越受到开发者青睐。Expo 是基于 React Native 的一整套工具和服务&#xff0c;它能够大幅降低原生开发的门槛&#xff0c;让开发者只需关注业务逻辑和界面实现&#xff0c;而不用纠结于复杂的原生配置。本文将从零开…

windows技术基础知识

NT架构 NT 就是new techonology 的英文单词缩写&#xff0c;是微软1993年推出操作系统的重大升级&#xff0c;如内存管理&#xff0c;安全机制&#xff0c;多任务&#xff0c;多线程支持。在此之前操作系统都是基于MS-DOS上面的图形化界面&#xff0c;只有有限的内存管理和多任…

迪杰斯特拉+二分+优先队列+拓扑+堆优化(奶牛航线Cowroute、架设电话线dd、路障Roadblocks、奶牛交通Traffic)

原文地址 https://fmcraft.top/index.php/Programming/2025040402.html 主要算法 迪杰斯特拉Dijkstra 题目列表 P1&#xff1a;奶牛航线Cowroute 题目描述 题目描述 Bessie已经厌倦了农场冬天的寒冷气候&#xff0c;她决定坐飞机去更温暖的地方去度假。不幸的是&#xf…

#Liunx内存管理# 在32bit Linux内核中,用户空间和内核空间的比例通常是3:1,可以修改成2:2吗?

在32位Linux内核中&#xff0c;用户空间和内核空间的3:1默认比例可以修改为2:2&#xff0c;但需要权衡实际需求和潜在影响。以下是具体分析&#xff1a; 一、修改可行性 1.技术实现 通过内核启动参数调整虚拟地址空间划分&#xff0c;例如在GRUB配置中添加mem2G参数&#xff0c…

JAVA:使用 Curator 进行 ZooKeeper 操作的技术指南

1、简述 Apache Curator 是一个基于 ZooKeeper 的 Java 客户端库&#xff0c;它极大地简化了使用 ZooKeeper 的开发工作。Curator 提供了高层次的 API&#xff0c;封装了很多复杂的 ZooKeeper 操作&#xff0c;例如连接管理、分布式锁、Leader 选举等。 在分布式系统中&#…