当前位置: 首页 > backend >正文

九,算法-递归

定义

递归函数就是调用自身的函数,无限递归是没有意义的,因此在递归中必须有基准情形。递归术语中,函数不再递归的情形就是基准情形。每个递归函数至少需要一个基准情形才能避免无限调用。

阅读递归代码

  • 辨认基准
  • 用基准情形分析代码步骤
  • 找出基准情形的前一步
  • 基于前一步情形分析代码步骤
  • 重复过程

从基准情形慢慢向上分析是理解递归的好方法。

递归的执行

计算机使用栈来调用函数,栈顶表示最近调用的函数。碰到无限递归时,计算机把同一个函数不断地压进调用栈,导致计算机的短期存储被耗尽,这就是栈溢出错误。

书写递归代码

首先,递归可以用来解决以下几类问题:

  • 重复执行的问题;如打印目录;
  • 计算;即根据子问题进行计算;以计算阶乘为例,可以使用循环,也可以使用递归,即子问题和原问题相同,但输入更小,如下:
unsigned long long factorialByCycle(int n) {if (n < 0) {throw std::invalid_argument("Negative numbers have no factorial.");}unsigned long long result = 1;for (int i = 2; i <= n; ++i) {result *= i;}return result;
}unsigned long long factorialByRecursion(int n) {if (n < 0) {throw std::invalid_argument("Negative numbers have no factorial.");}if (n == 0 || n == 1) {return 1;}return n * factorialByRecursion(n - 1);
}//from bottom to top situation
unsigned long long factorialByRecursion2(int n, int i=0, int result = 1) {if (n < 0) {throw std::invalid_argument("Negative numbers have no factorial.");}if (n > i) {return result;}return factorialByRecursion2(n, i+1, result*i);
}

以上两种实现阶乘的方式,也代表了两种计算方法,即:自下而上得出答案(迭代或者递归)和自上而下得出答案(递归实现,且是唯一方法)。自上而下的方法如果用递归实现,则需要额外参数,如示例中的factorialByRecursion2。

自上而下递归

递归提供了一种不同的角度来思考问题,即可以在不知道如何解决问题的情况下解决该问题,其过程如下:

  1. 将要实现的递归函数当成已经实现完成的函数,可以直接使用;
  2. 分辨子问题;
  3. 在子问题上调用递归函数,查看其结果,并以此为基础继续。

当需要实现一些比较复杂的问题时,可以通过递归帮助思考(即自上而下)。以台阶问题为例:假设有N级台阶,一个人每次可以上1级、2级、3级台阶。那么爬到顶端,共有多少种路径。

如果以自下而上的方式研究这个问题,则情况会变得复杂,以爬上4级台阶为例,自下而上的分析过程如下:

  • 只有一级台阶,则路径只有一种;
  • 只有两级台阶,路径有2种路径,即
    1,1
    2
  • 只有三级台阶,路径有4种路径,即
    1,1,1
    1,2
    2,1
    3

    如果四级台阶,则有7种路径。如果要爬11级台阶,则用这种方式穷举,将会比较复杂、费劲。

如果使用递归的自上而下的思考方法,那么我们先要分析子问题即可。以11级阶梯为例:

  • 因为每次爬取的阶梯数是1级、2级和3级,则爬到11级台阶则可能有三种情况:从第10级台阶直接爬上来;从第9级台阶直接爬上来;从第8级台阶直接爬上来;
  • 先分析从第10级台阶直接爬上来,如果从第10级台直接阶爬到第11级台阶,那么就不可能从第9级台阶直接爬到第11级台阶,同理从第9级台阶直接爬到第11级台阶,那么这条路径就不可能包括从第10级台阶爬到第11级台阶这一步;
  • 再分析从第9级台阶直接爬上来,如果从第9级台阶直接爬到第11级台阶,那么就不可能从第8级台阶直接爬到第11级台阶,同理从第8级台阶直接爬到第11级台阶,那么这条路径就不可能包括从第9级台阶爬到第11级台阶这一步;

综上,爬到11级顶楼的路径是爬到第10、9、8级台阶的路径之和,除此之外没有其他可能路径,因为从第7级无法直接到达第11级台阶,因此函数定义如下:

int numberOfPaths(int n) {return numberOfPaths(n-1) + numberOfPaths(n-2) + numberOfPaths(n-3);
}

接下来就是要确认基准情形。按照对子问题的分析,其基准问题即位n=3,2,1时的情景。一种方式是硬编码:

int numberOfPaths(int n) {if (n < 0) {return 0;}else if (n == 1) {return 1;}else if (n == 2) {return 2;}else if (n == 3) {return 4;}return numberOfPaths(n-1) + numberOfPaths(n-2) + numberOfPaths(n-3);
}

还有一种方式是,由结果反推,首先是最基准的情形,即n=1时,返回1;当n=2时,返回2,其结果由numberOfPaths(1) + numberOfPaths(0) +numberOfPaths(-1)决定,由于numberOfPaths(1)=1,则可以推导numberOfPaths(0) +numberOfPaths(-1)= 1,假设numberOfPaths(0) = 1,则numberOfPaths(-1)=0;当n=3时,返回4,其结果由numberOfPaths(2) + numberOfPaths(1) +numberOfPaths(0)决定,由于numberOfPaths(2) = 2,numberOfPaths(1) = 1,numberOfPaths(0) = 1,由此计算numberOfPaths(-1)=0,则前后吻合,按照这种分析则可以得出另一种形式的代码:

int numberOfPath2s(int n) {if (n < 0) {return 0;}else if (n == 1 || n == 0) {return 1;}return numberOfPaths(n - 1) + numberOfPaths(n - 2) + numberOfPaths(n - 3);
}

http://www.xdnf.cn/news/17527.html

相关文章:

  • ​电风扇离线语音芯片方案设计与应用场景:基于 8 脚 MCU 与 WTK6900P 的创新融合
  • Spark 优化全攻略:从 “卡成 PPT“ 到 “飞一般体验“
  • Empire--安装、使用
  • 布控球:临时布防场景的高清回传利器-伟博
  • 人工智能-python-机器学习-逻辑回归与K-Means算法:理论与应用
  • PYTHON开发的实现运营数据大屏
  • OFD一键转PDF格式,支持批量转换!
  • pip 和 conda,到底用哪个安装?
  • golang开源库之LaPluma
  • 是否有必要使用 Oracle 向量数据库?
  • Oracle 19C 配置TAF
  • CLIP,BLIP,SigLIP技术详解
  • 分治-归并-912.排序数组-力扣(LeetCode)
  • 机器学习——K-means聚类
  • IPCP(IP Control Protocol,IP控制协议)
  • Apache Ignite 生产级的线程池关闭工具方法揭秘
  • 【运维进阶】LAMPLNMP 最佳实践
  • 疯狂星期四文案网第36天运营日记
  • WNZ-20转速扭矩试验台
  • PHP request文件封装
  • 小杰python高级(three day)——matplotlib库
  • ESP32 配合上位机串口打印数据
  • Python面试题及详细答案150道(41-55) -- 面向对象编程篇
  • linux安装和使用git
  • CVE-2019-0708复刻
  • SpringBoot 实现 Excel 导入导出功能的三种实现方式
  • [激光原理与应用-240]:光学器件 - 变形镜,波前校正器
  • 数据结构:树与二叉树
  • python之浅拷贝深拷贝
  • Java Selenium 自动打开浏览器保存截图