估计找工作的,都会碰到面试官老是问道“递归算法”,感同身受,前段时间面试的时候,就有一家问道这个问题,是非常典型的问题。在前面一篇世界上有哪些代码量很少,但很牛逼很经典的算法或项目案例?,递归应该算是比较“经典”的算法。

1.从 斐波那契数列开始说起

波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,是指这样一个数列

递推公式如图:

递推公式

有没有直接计算的公式?当然有

public int Fibo(int n){double c = Math.Sqrt(5);return (int)((Math.Pow((1 + c) / 2, n) - Math.Pow((1 - c) / 2, n)) / c);}

1.最常见的递归

第一项和第二项的值均为1,后面每项的值都是前两项值之和,所以我们很多人基本上都会使用递归来实现,常见的算法如下:

        public int Fibo(int n)        {if (n == 1 || n == 2)           {return 1;           }return Fibo(n - 2) + Fibo(n - 1);        }

但这种做法并不能完全解决问题,因为最大允许的递归深度跟当前线程剩余的栈空间大小有关,事先无法计算。如果实时计算,代码过于复杂,就会影响代码的可读性。所以,如果最大深度比较小,比如 10、50,就可以用这种方法,否则这种方法并不是很实用。

ps:递归代码要警惕重复计算

除此之外,使用递归时还会出现重复计算的问题。刚才我讲的第二个递归代码的例子,如果我们把刚才我讲的第二个递归代码的例子,如果我们把整个递归过程分解一下的话,那就是这样的:n 越大,这段代码执行效率越低通过测试一下看他的效率如何

            Stopwatch sw = new Stopwatch();            sw.Start();var result = Fibo(40);            sw.Stop();            Debug.WriteLine("n=100;result="+result+";耗时:"+sw.ElapsedMilliseconds);
n=10;result=55;耗时:2715n=40;result=102334155;耗时:4673

如果n再稍微大一点,所消耗的时间是成指数级增长的,比如n=64的时候,所消耗的时间可能是两三年!不信的话,你可以试试!

我们会发现f(n)这个方法被调用了很多次,而且其中重复率非常之高,也就是说被重复计算了很多次,如果n稍微大一点这棵树会非常庞大。这里我们可以看出,每个节点就需要计算一次,总计算的次数就是该二叉树节点的数量,可见其时间复杂度为O(2n),是指数级的,其空间复杂度也就是该二叉树的高度,为O(n)。这样来看,我们应该就清楚了,为什么这段代码效率如此低下了吧。

2.数组保存法

我们应该避免无数次重复的计算

为了避免无数次重复,可以从n=1开始往上计算,并把每一个计算出来的数据,用一个数组保存,需要最终值时直接从数组中取即可,算法如下:

public int fib(int n) {int[] fib = new int[n];    fib[0] = 1;    fib[1] = 1;for (int i = 2; i < n; i++) {        fib[i] = fib[i - 2] + fib[i - 1];    }return fib[n - 1];}

测试一下结果

n=10;result=55;耗时:0n=40;result=102334155;耗时:0n=1000000;result=1884755131;耗时:5

毫秒级,几乎忽略不计的,当计算100万时,也就5毫秒

3.滚动数组法

尽管上述算法已经很高效了,但我们还是会发现一个问题,其实整个数组中,每次计算时都只需要最新的3个值,前面的值计算完后就不再需要了。比如,计算到第10次时,需要的数组空间只有第8和第9两个空间,前面第1到第7个空间其实就不再需要了。所以我们还可以改进,通过3个变量来存储数据,算法如下:

        public int Fibo3(int n)        {int first = 1;int second = 1;int third = 2;for (int i = 3; i <= n; i++)            {                third = first + second;                first = second;                second = third;            }return third;        }

时间复杂度仍然为O(n),而空间复杂度为常量级别3,即空间复杂度为0,所以这种方法是非常高效的。

3.尾递归法

首先我们来了解一下什么是尾调用。

在计算机科学里,尾调用是指一个函数里的最后一个动作是一个函数调用的情形:即这个调用的返回值直接被当前函数返回的情形。这种情形下该调用位置为尾位置。

 /// n 第n个数/// first 第n个数/// second 第n与第n+1个数的和/// @return 返回斐波那契数列值public int Fib5(int n, int first, int second) {if (n <= 1) {return first;    } else {return fib5(n-1,second,first+second);    }}

,也都是通过两个变量保存计算值,传递给下一次进行计算,递归的过程中也是根据n值变化逐步重复运算,和循环差不多,时间复杂度和空间复杂度也都一样,优雅了很多。

我们知道递归调用是通过栈来实现的,每调用一次函数,系统都将函数当前的变量、返回地址等信息保存为一个栈帧压入到栈中,那么一旦要处理的运算很大或者数据很多,有可能会导致很多函数调用或者很大的栈帧,这样不断的压栈,很容易导致栈的溢出。

还有一种优化思路就是矩阵快速幂法,可参考链接 https://blog.csdn.net/computer_user/article/details/86927209

c++函数不允许递归_递归优化的这三种方式你知道吗?相关推荐

  1. java 异步事件_处理异步事件的三种方式

    在网站开发中,异步事件是项目必然需要处理的一个环节,也因为前端框架的兴起,通过框架实现的 SPA 已经是快速建构网站的标配了,一部获取数据也就成了不可或缺的一环:本文来就讲一讲 JavaScript ...

  2. 递归优化的这三种方式你知道吗?

    估计找工作的,都会碰到面试官老是问道"递归算法",感同身受,前段时间面试的时候,就有一家问道这个问题,是非常典型的问题.在前面一篇世界上有哪些代码量很少,但很牛逼很经典的算法或项目 ...

  3. Php斐波那契数列尾递归优化,递归优化的这三种方式你知道吗?

    估计找工作的,都会碰到面试官老是问道"递归算法",感同身受,前段时间面试的时候,就有一家问道这个问题,是非常典型的问题.在前面一篇世界上有哪些代码量很少,但很牛逼很经典的算法或项目 ...

  4. quartz mysql数据源_配置quartz数据源的三种方式

    如果是使用了JDBC JobStore或JobStoreCMT获得持久的Job时,就要配置相关的数据源了. 方式一:使用quartz.properties文件,这时只需要在property文件中增加如 ...

  5. 详解python运行三种方式_详解python运行三种方式

    方式一 交互式编程 交互式编程不需要创建脚本文件,是通过 Python 解释器的交互模式进来编写代码. linux上你只需要在命令行中输入 Python 命令即可启动交互式编程,提示窗口如下: $ p ...

  6. python的运行方式_详解python运行三种方式

    方式一 交互式编程 交互式编程不需要创建脚本文件,是通过 Python 解释器的交互模式进来编写代码. linux上你只需要在命令行中输入 Python 命令即可启动交互式编程,提示窗口如下: $ p ...

  7. JavaScript高级第2天:定义函数的三种方式、函数的原型链结构、完整原型链、作用域以及作用域链、函数的四种调用模式、闭包、计数器、斐波那契数列优化、三种继承方式

    JavaScript高级第二天 01-定义函数的三种方式 1.函数声明 function:可以先调用再声明,因为预解析(把函数声明.变量声明进行提升) function fn() {//函数体conl ...

  8. 运行python程序的两种方式交互式和文件式_执行Python程序的两种方式

    交互式(了解) 交互式环境下,敲完一条命令按下enter键马上能看到结果,调试程序方便.程序无法永久保存,关掉cmd窗口数据就消失了. 命令行式(了解) 打开文本编辑器,在文本编辑器中写入一串字符. ...

  9. python读取图像数据流_浅谈TensorFlow中读取图像数据的三种方式

    本文面对三种常常遇到的情况,总结三种读取数据的方式,分别用于处理单张图片.大量图片,和TFRecorder读取方式.并且还补充了功能相近的tf函数. 1.处理单张图片 我们训练完模型之后,常常要用图片 ...

最新文章

  1. js+php在线截图 jquery fileupload.js,另一种图片上传 jquery.fileupload.js
  2. sql 命令使用简单记录
  3. 使用ob_gzhandler函数有3种方法让它对php进行压缩
  4. python以二进制读取的文件显示b'b'_python - Python读取二进制文件并解码 - 堆栈内存溢出...
  5. pandas 字段操作
  6. @EnableAsync annotation metadata was not injected
  7. Golang slice高级应用
  8. 数据挖掘:银行评分卡制作——数据分箱、WOE、IV的意义
  9. FeelYourSound Chillout Engine Pro for Mac - MIDI文件生成插件
  10. AFNetworking 2.0 来了
  11. 39. 组合总和:给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列
  12. RT-Thread邮箱
  13. An element could not be located on the page using the given search parameters.
  14. 用户需求调研—快速上手篇
  15. github网站扩展增强
  16. CentOS 6.7 源码搭建LNMP架构部署动态网站环境
  17. 软件测试应届简历,软件测试应届毕业生个人简历
  18. 使用PyTorch实现鸟类音频检测卷积网络模型
  19. 面试mysql之SQL优化总结一:索引的使用
  20. 平面几何----用梅涅劳斯定理证明笛沙格定理

热门文章

  1. Nginx+Tomcat搭建高性能负载均衡集群
  2. 跳转前暂停几秒js如何实现
  3. ajax修改属性后如何遍历,Ajax遍历jSon后对每一条数据进行相应的修改和删除(代码分享)...
  4. 深入大数据安全分析(1):为什么需要大数据安全分析?
  5. 系统搜索资源就停止服务器,SQL Server (MSSQLSERVER) 服务启动不了,系统日志显示由于下列服务特定错误而终止: 找不到映像文件中指定的资源名。...
  6. linux无网络环境pcre安装,Linux下无网络安装Nginx
  7. 正宗PC Unix实验环境
  8. 用计算机模拟宇宙,计算机中的宇宙
  9. Silverlight 2.5D RPG游戏技巧与特“.NET技术”效处理:(十一)AI系统
  10. ajax嵌套ajax的坏处,promise解决ajax的多重嵌套