数据结构和算法|递归算法那些事(递归算法的时间复杂度、尾递归优化、斐波那契数列)

对于文章的第一部分,递归算法的时间复杂度,来自于代码随想录文章:通过一道面试题目,讲一讲递归算法的时间复杂度!
对于第二节尾递归优化来自于B站:尾递归优化:你的递归调用是如何被优化的?

文章目录

  • 关于递归算法的时间复杂度
  • 尾递归优化
  • 斐波那契数列
    • 简单的递归方法
    • 动态规划的方法

关于递归算法的时间复杂度

对于题目:求 x 的 n 次方,首先请给出最简单的方法——迭代版本:

int function1(int x, int n) {int res = 1;for (int i = 0; i < n; i++) {res *= x;}return res;
}

这里的时间复杂度为 O(n),那么请问我们有没有时间复杂度更低的方法呢?

递归!好了我们先尝试一下递归。

int function(int x, int n) {if (n == 0) return 1;return function(x, n - 1) * x; 
}

递归算法的时间复杂度本质上是要看: 递归的次数 * 每次递归中的操作次数。

每次n-1,递归了n次时间复杂度是O(n),每次进行了一个乘法操作,乘法操作的时间复杂度一个常数项O(1),所以这份代码的时间复杂度是 n × 1 = O(n)。

那么,还有这样一个版本的递归算法:

int function(int x, int n) {if (n == 0) return 1;if (n == 1) return x;if (n % 2 == 1) return function(x, n / 2) * function(x, n / 2) * x;return function(x, n / 2) * function(x, n / 2)
}

关于该递归函数的时间复杂度分析,就需要搬出我们的二叉树进行辅助了。

当前这棵二叉树就是求x的n次方,n为16的情况,n为16的时候,进行了多少次乘法运算呢?

这棵树上每一个节点就代表着一次递归并进行了一次相乘操作,所以进行了多少次递归的话,就是看这棵树上有多少个节点。

熟悉二叉树话应该知道如何求满二叉树节点数量,这棵满二叉树的节点数量就是 2 3 + 2 2 + 2 1 + 2 0 = 15 2^3 + 2^2 + 2^1 + 2^0 = 15 23+22+21+20=15,可以发现:这其实是等比数列的求和公式,这个结论在二叉树相关的面试题里也经常出现。

所以,如果是求 x 的 n 次方,那么时间复杂度就是 O(n)

那么,我们应该如何写出 O(logn) 的递归算法呢?

int function(int x, int n) {if (n == 0) return 1;if (n == 1) return x;int t = function4(x, n / 2);// 这里相对于function3,是把这个递归操作抽取出来if (n % 2 == 1) {return t * t * x;}return t * t;
}

依然还是看他递归了多少次,可以看到这里仅仅有一个递归调用,且每次都是n/2 ,所以这里我们一共调用了log以2为底n的对数次。

每次递归了做都是一次乘法操作,这也是一个常数项的操作,那么这个递归算法的时间复杂度才是真正的O(logn)。

尾递归优化

这里的函数是写计算阶乘的函数:

// 普通递归版本
int factorial(int n) {if (n <= 1) return 1;return factorial(n - 1) * n;
}// 迭代版本
int factorial(int n) {int acc = 1;while (n > 0) {acc *= n;n -= 1;}return acc;
}// 尾递归版本
int factorial(int n) {if (n <= 1) return acc;return factorial(n - 1, acc * n);
}

尾递归优化既可以是语言级别的,也可以是编译器级别的,我们的 C++ 就是编译器级别的尾递归优化。

斐波那契数列

力扣题目链接

简单的递归方法

class Solution {
public:int fib(int n) {if (n == 0) return 0;if (n == 1) return 1;return fib(n - 1) + fib(n - 2);}
};

从上文的分析我们可以看到,我们想要计算出该递归算法的时间复杂度,需要进行明确递归树。

下面我们来分析其节点个数:
• 计算 fib(n) 需要计算 fib(n-1) 和 fib(n-2)。
• 计算 fib(n-1) 需要计算 fib(n-2) 和 fib(n-3)。
• 计算 fib(n-2) 需要计算 fib(n-3) 和 fib(n-4)。
为了计算时间复杂度,我们需要了解递归树的节点数。递归树的节点数代表了函数调用的总次数。由于每个节点会生成两个子节点,递归树的节点数随着树的高度呈指数增长。

• 根节点 fib(n) 有两个子节点,分别是 fib(n-1) 和 fib(n-2)。
• 每个子节点再生成两个子节点,形成更多的函数调用。

递归树的高度大约为 n,因为我们从 fib(n) 一直递归到 fib(0) 或 fib(1)。所以,树的总节点数大约是 2 的 n 次方(2^n),这是因为每个节点生成两个子节点的过程类似于二叉树的完全展开。

综上所述,时间复杂度为: O(n)

动态规划的方法

由于斐波那契满足明显的递推关系,所以我们可以很直观得使用动态规划进行解题:

我们先逐一分析动态规划五部曲:

  • dp数组的含义:dp[i]表示的是第 i 个数列的值
  • 确定递推公式:dp[i] = dp[i - 1] + dp[i - 2]
  • dp数组如何初始化:dp[0] = 0 dp[1] = 1
  • 确定遍历顺序:就直接往后遍历就可以了
  • 打印 dp 数组进行检查。
class Solution {
public:int fib(int n) {if (n < 2) return n;vector<int> dp(n, 1);for (int i = 2; i < n; i++) {dp[i] = dp[i - 1] + dp[i - 2];}return dp[n - 1];}
};

随后我们可以改进代码,也就是使用一种类似于滚动数组的思路,因为我们的递推公式只和前一个或者前两个有关,所以我们可以写如下代码:

class Solution {
public:int fib(int n) {if (n < 2) return n;vector<int> dp({0, 1, 1});for (int i = 2; i < n; i++) {int tmp = dp[1] + dp[2];dp[0] = dp[1];dp[1] = dp[2];dp[2] = tmp;} return dp[2];}
};

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

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

相关文章

XML(可扩展标记语言)

QDomDocument doc;QDomElement ss doc.createElement("root");//创建标签 //ss标签添加到文档对象doc.appendChild(ss);//doc.save()auto hero doc.createElement("hero");ss.appendChild(hero);hero.setAttribute("id",10086);//为hero添加属…

MySQL——数据表的基本操作(一)创建数据表

数据库创建成功后,就需要创建数据表。所谓创建数据表指的是在已存在的数据库中建立新表。需要注意的是&#xff0c;在操作数据表之前&#xff0c;应该使用 “ USE 数据库名 ” 指定操作是在哪个数据库中进行&#xff0c;否则会抛出 “ No database selected ” 错误。创建数据表…

Tomcat 使用和配置文件(详解)

一.tomcat 介绍 1. tomcat 概述 自从JSP发布之后&#xff0c;推出了各式各样的JSP引擎。Apache Group在完成GNUJSP1.0的开发以后&#xff0c;开始考虑在SUN的JSWDK基础上开发一个可以直接提供Web服务的JSP服务器&#xff0c;当然同时也支持 Servlet&#xff0c;这样Tomcat就诞…

[自学记录09*]关于模糊效果降采样优化性能的小实验

一、降采样在模糊中的优化 这两天接手了几个高度定制化的模糊&#xff0c;包括不限于放射和旋转状的径向模糊&#xff0c;移轴模糊&#xff0c;景深的散景模糊等等&#xff0c;这些效果在游戏中非常常见。 其实模糊的原理都差不多&#xff0c;无非就是对UV偏移后重新采样再求…

《Python爬虫逆向实战》绕过debugger的方法汇总

禁用断点 打开控制台&#xff0c;点击右边的禁用断点按钮。 点击之后再刷新下&#xff0c;就会发现debugger失效了。 注&#xff1a;这种方法有个 弊端&#xff0c;就是我们在代码中下的断点也都将失效。 Add script to ignore list 在代码文件中任意位置右键&#xff0c;然…

51单片机—串口

一、 串口基本认知 串行接口简称串口&#xff0c;也称串行通信接口或串行通讯接口&#xff08;通常指COM接口&#xff09;&#xff0c;是采用串行通信方 式的扩展接口。串行接口&#xff08;Serial Interface&#xff09;是指数据一位一位地顺序传送。其特点是通信线路简 单&a…

【C++ 面试 - 基础题】每日 3 题(七)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

【网络安全】玲珑安全第四期

鉴于玲珑安全漏洞挖掘前三期课程取得的优异成绩和获得的强烈反响,我们决定启动玲珑安全第四期漏洞挖掘培训计划。 文章目录 往期学员收获基础学员报喜(部分)课程反馈第四期课程课程内容免费课程往期学员收获 第一期课程总结及学员收获:->点我查看第一期学员收获<- …

性能测试工具LoadRunner

前言&#x1f440;~ 上一章我们介绍了性能测试的一些基本概念&#xff0c;重要的是性能测试的各项指标&#xff0c;今天我们使用性能测试工具LoadRunner简单的完成一次性能测试 性能测试Load Runner LoadRunner是什么&#xff1f; LoadRunner安装 LoadRunner脚本录制 1.录…

算法板子:质数——判定质数、分解质因数、筛质数

目录 一、判定质数 1. 代码 二、分解质因数 1. 质因数的概念 2. 代码 三、筛质数——获取1~n中所有质数的个数 1. 合数的概念 2. 代码 一、判定质数 1. 代码 #include <iostream> using namespace std;bool is_prime(int x) {// 1不是质数, 需要特判if (x 1) …

QT键盘和鼠标事件

这些事件都在QWidget 中的保护成员方法中 都是虚函数在头文件中声明了 需要类外重现实现 如果头文件中声明 类外无实现就会报错 void Widget::keyPressEvent(QKeyEvent *event) {switch (event->key()) {//获取按键case Qt::Key_W://按键wqDebug()<<"按下w"…

开源免费前端地图开发组件xdh-map

xdh-map是一个基于Openlayers的地图应用Vue组件&#xff0c;具有多方面的功能和特点。以下是对xdh-map的详细介绍&#xff1a; 一、功能与特性 内置多种地图瓦片&#xff1a;xdh-map内置了百度、高德、天地图等地图瓦片&#xff0c;使得开发者可以方便地在应用中集成多种地图…

【Material-UI】Checkbox 组件中的 Label Placement 设置详解

文章目录 一、Checkbox 组件简介1. 组件概述2. labelPlacement 属性 二、labelPlacement 属性的使用方法三、各标签位置的效果与应用场景1. Top&#xff08;顶部&#xff09;2. Start&#xff08;左侧&#xff09;3. Bottom&#xff08;底部&#xff09;4. End&#xff08;右侧…

大模型算力基础设施技术趋势、关键挑战与发展路径

文章目录 前言一、大模型技术发展趋势1.1 大语言模型1.2 多模态模型1.3 长序列模型1.4 混合专家模型二、大模型算力基础设施发展问题与挑战2.1 可用算力规模亟需算力利用效率提升2.2 集群性能提升依赖跨尺度、多层次互联三、大模型算力基础设施高质量发展路径总结前言 从大模型…

使用 `grep` 命令的常用方式

使用 grep 命令的常用方式 grep 是一个强大的命令行工具&#xff0c;用于在文件中搜索文本。无论是程序员、系统管理员还是普通用户&#xff0c;都可以通过 grep 快速定位需要的信息。本文将介绍 grep 命令的一些常用方式&#xff0c;并给出相应示例的执行结果。 示例文本 在…

C语言求平方和倒数

文章目录 1. 代码实现float类型数据double类型数据使用 double 类型的调整 2. 魔数与位级别操作浮点数表示位级别魔数操作 3. 牛顿迭代4. 复杂代码具体解释具体解释&#xff1a;目的&#xff1a;举例&#xff1a; 5.感谢 平方和倒数 广泛用于计算机图形学中&#xff0c;尤其是在…

Spring Boot - 通过ApplicationListener实现接口请求的性能监控

文章目录 概述1. ServletRequestHandledEvent事件2. 实现步骤3. 优缺点分析4. 测试与验证小结其他方案1. 自定义拦截器2. 性能监控平台3. 使用Spring Boot Actuator4. APM工具 概述 在Spring框架中&#xff0c;监控接口请求的性能可以通过ServletRequestHandledEvent事件实现。…

【数据结构】—— 内部排序算法详解

1、前言2、常见排序算法3、排序算法实现3.1 直接插入排序3.2 希尔排序3.3 选择排序3.4 堆排序3.5 冒泡排序3.6 快速排序3.6.1 单趟排序hoare法挖坑法双指针法 3.6.2 非递归实现3.6.3 常见问题基准值的选取小区间优化 3.7 归并排序3.7.1 递归实现3.7.2 非递归实现 3.8 计数排序 …

glibc的安装及MySQL的安全用户角色权限(twenty-one day)

一、glibc安装 mysql 清空/etc/目录下的my.cnf ls -l /etc/my.cnf rm -rf /etc/my.cnf yum -y remove mariadb find / -name "*mysql*" -exec rm -rf {} \; 安装mysql软件包 wget https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.33-li nux-glibc2.1…

面壁的智能开源 MiniCPM-V 2.6 边缘人工智能多模态功能与 GPT-4V 不相上下

"MiniCPM-V2.6 "是一个边缘多模态人工智能模型&#xff0c;仅拥有 80 亿个参数&#xff0c;却在单图像、多图像和视频理解任务中取得了低于 200 亿个参数的三项 SOTA&#xff08;艺术境界&#xff09;成绩&#xff0c;显著增强了边缘多模态能力&#xff0c;并与 GPT-…