尾递归 递归函数中,递归调用是整个函数体中最后的语句,且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归,空间复杂度是O(1)
什么是递归深度
递归深度就是递归函数在内存中,同时存在的最大次数。
例如下面这段求阶乘的代码:
Java:
int factorial(int n) {
if (n == 1) {
return 1;
}
return factorial(n - 1) * n;
}
Python:
def factorial(n):
if n == 1:
return 1
return factorial(n-1) * n
C++:
int factorial(int n) {
if (n == 1) {
return 1;
}
return factorial(n - 1) * n;
}
当n=100
时,递归深度就是100。一般来说,我们更关心递归深度的数量级,在该阶乘函数中递归深度是O(n)O(n)O(n),而在二分查找中,递归深度是O(log(n))O(log(n))O(log(n))。在后面的教程中,我们还会学到基于递归的快速排序、归并排序、以及平衡二叉树的遍历,这些的递归深度都是(O(log(n))(O(log(n))(O(log(n))。注意,此处说的是递归深度,而并非时间复杂度。
太深的递归会内存溢出
首先,函数本身也是在内存中占空间的,主要用于存储传递的参数,以及调用代码的返回地址。
函数的调用,会在内存的栈空间中开辟新空间,来存放子函数。递归函数更是会不断占用栈空间,例如该阶乘函数,展开到最后n=1
时,内存中会存在factorial(100), factorial(99), factorial(98) ... factorial(1)
这些函数,它们从栈底向栈顶方向不断扩展。
当递归过深时,栈空间会被耗尽,这时就无法开辟新的函数,会报出stack overflow
这样的错误。
所以,在考虑空间复杂度时,递归函数的深度也是要考虑进去的。
Follow up:
尾递归:若递归函数中,递归调用是整个函数体中最后的语句,且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。(上例factorial函数满足前者,但不满足后者,故不是尾递归函数)
尾递归函数的特点是:在递归展开后该函数不再做任何操作,这意味着该函数可以不等子函数执行完,自己直接销毁,这样就不再占用内存。一个递归深度O(n)O(n)O(n)的尾递归函数,可以做到只占用O(1)O(1)O(1)空间。这极大的优化了栈空间的利用。
但要注意,这种内存优化是由编译器决定是否要采取的,不过大多数现代的编译器会利用这种特点自动生成优化的代码。在实际工作当中,尽量写尾递归函数,是很好的习惯。
而在算法题当中,计算空间复杂度时,建议还是老老实实地算空间复杂度了,尾递归这种优化提一下也是可以,但别太在意。
尾递归 递归函数中,递归调用是整个函数体中最后的语句,且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归,空间复杂度是O(1)的更多相关文章
- VC/MFC中通过CWebPage类调用javascript函数(给js函数传参,并取得返回值)
转自:http://www.cnblogs.com/javaexam2/archive/2012/07/14/2632959.html ①需要一个别人写好的类CWebPage,将其对于的两个文件Web ...
- VC与JavaScript交互(三) --- CWebPage类调用javascript函数(给js函数传参,并取得返回值)
①需要一个别人写好的类CWebPage,将其对于的两个文件WebPage.h和WebPage.cpp添加到工程中. ②添加WebBrowser控件,在视图/对话框类的头文件中#include &quo ...
- jQuery find() 搜索所有段落中的后代 C# find() 第一个匹配元素 Func 有返回值 Action是没有返回值 Predicate 只有一个参数且返回值为bool 表达式树Expression
所有p后代span Id为 TotalProject 的 select 标签 的后代 option标签 为选中的 text using System; using System.Collections ...
- 调用个别f5 负载端口为80的vs时,返回值为空的问题
现状: vs负载端口为80并添加XFF,pool包含2个member,member的monitor端口为80&9000. 故障现象: 应用同事描述说再完全复制了一个member并添加到pool ...
- EF5中 执行 sql语句使用Database.ExecuteSqlCommand 返回影响的行数 ; EF5执行sql查询语句 Database.SqlQuery 带返回值
一: 执行sql语句,返回受影响的行数 在mysql里面,如果没有影响,那么返回行数为 -1 ,sqlserver 里面 还没有测试过 using (var ctx = new MyDbConte ...
- stl中的transform()注意其与for_each的不同点(有无返回值)
#include<iostream> using namespace std; #include"vector" #include"algorithm&quo ...
- JS中函数的本质,定义、调用,以及函数的参数和返回值
要用面向对象的方式去编程,而不要用面向过程的方式去编程 对象是各种类型的数据的集合,可以是数字.字符串.数组.函数.对象…… 对象中的内容以键值对方式进行存储 对象要赋值给一个变量 var cat={ ...
- .NET中如何在同步代码块中调用异步方法
更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 在同步代码块中调用异步方法,方法有很多. 一.对于有返回值的Task 在同步代码块中直接访问 Task 的 Result ...
- shell调用函数返回值深入分析
编写shell脚本过程中,我们经常会自定义一些函数,并根据函数的返回值不同来执行相应的流程,那么我们如何来获取函数的返回值呢? 首先shell中调用函数有两种方式: 第一种:value=`functi ...
随机推荐
- FreeSql 访问 Oracle 解决大小写问题
方法一 new FreeSqlBuilder() .UseSyncStructureToUpper(true) .Build() 方法二 全局转换实体属性名方法,这种只能转属性. 其实是通过Aop方法 ...
- Python UDP小程序
为了做UDP的测试,采用了nc和Python的服务器端. nc的安装和使用: yum install -y nc nc -vuz Python的UDP服务器端小程序: # -*- coding: UT ...
- AI保存文件出现了未知错误 (Adobe Illustrator)
可能的原因如下:1 把文件中所有的文字全选,转成曲线,把所有的文字都创建成轮廓2 新建一个页面,把制作的复制到新页面里3 需要把图层全部解锁再保存4 没有权限,设置一下即可5 PRINT SPOOLE ...
- Orm 配置说明
一.在线技术文档: http://files.cnblogs.com/files/humble/d.pdf 二.使用的大致流程 1.首先下载代码生成器,可以一键生成项目Model层;(其中含有 ...
- QML学习(五)——<TextInput和TextEdif输入框>
这一篇来看两个用于文本输入的项目,分别是作为单行文本输入的 TextInput 和多行文本输入的 TextEdit . 下面开始教程. TextInput TextInput 项目用来显示单行可编辑的 ...
- cad 一个小技巧--复制视口带冻结信息
cad使用 ctrl+c 和 ctrl+v 进行跨文件复制视口的时候,会出现复制视口冻结信息丢失,因为你只选择了视口进行复制, 如果要实现带上冻结信息,那么要把含有相关图层的图元一起 ctrl+c/v ...
- 自定义标签&JSON数据处理
最近几日去参加一些面试,多多少少有一些收获. 现将遇到的一些面试题,做一下分析和总结. 1.使用原生JS,不能使用递归,查找dom中所有以“<com-”开头的自定义标签tagName. < ...
- [转帖]Java Netty简介
Java Netty简介 https://www.cnblogs.com/ghj1976/p/3779820.html Posted on 2014-06-10 13:41 蝈蝈俊 阅读(2992) ...
- 使用scrapy框架做武林中文网的爬虫
一.安装 首先scrapy的安装之前需要安装这个模块:wheel.lxml.Twisted.pywin32,最后在安装scrapy pip install wheel pip install lxml ...
- Linux平台上常用到的c语言开发程序
Linux操作系统上大部分应用程序都是基于C语言开发的.小编将简单介绍Linux平台上常用的C语言开发程序. 一.C程序的结构1.函数 必须有一个且只能有一个主函数main(),主函数的名为main. ...