进一步讨论递归函数——递归与栈

  

递归函数,在函数的执行函数中,需多次进行自我调用。那么,递归函数是如何执行的?先看任意两个函数之间进行调用的情形。用函数和被调用函数[若在函数A中调用了函数B,则称函数A为调用函数,称函数B为被调用函数。]之间的链接及信息交换需通过栈来进行。在上一篇递归函数的讲解中主要对递归的定义和一些应用进行了介绍,最近学习了一点数据结构的知识,看到了递归函数的工作原理其实使用栈来实现的我才恍然大悟。知识学多了,真的可以连接到一起的。

  通常,当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需先完成3件事:(1)将所有的实在参数、返回地址等信息传递给被调用函数保存;(2)为被调用函数的局部变量分配存储区;(3)将控制转移到被调函数的入口。而从被调用函数返回调用函数之前,系统也应完成3件工作:(1)保存被调函数的计算结果;(2)释放被调函数的数据区;(3)依照被调函数保存的返回地址将控制转移到调用函数。当有多个函数构成嵌套调用时,按照“后调用先返回”的原则,上述函数之间的信息传递和控制转移必须通过“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放它的存储区,则当前正运行的函数的数据区必在栈顶。

  就像是下面这个Fibonacci函数的运算原理,就是利用了函数调用函数,在栈顶分配存储区,因为栈是一种运算受限制的线性表,采用的是先进先出的方法,在栈顶一端进行操作,先调用的,先分配,当退出的时候,先释放的时候最顶上的一次调用空间,也就是最后一次调用。这就很好的解释了递归的一层一层的调用关系。

递归优点:

  结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性

递归缺点:

  递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多关于递归与非递归的效率,空间复杂度的话,非递归完胜。时间复杂度要具体问题具体分析,一般来说递归的执行步数较多,非递归的执行频率较多。 在用栈的情况下,非递归确实不如递归的效率。

什么时候递归用到栈?

   除内联函数外,调用函数的时候对应的汇编代码必然有。call 函数地址(传参方式根据调用方式和对应语言有所不同哈)。其中call命令的含义就是把返回地址打入栈中然后跳到函数地址执行,因此必然会使用栈。

总结:

  J2SE和数据结构一起复习,碰撞出新的火花,快来发现吧。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-14 13:22:16

进一步讨论递归函数——递归与栈的相关文章

函数递归与栈的关系

首先通过反汇编语言,我们来了解一下最简单的递归函数与栈之间的关系. 如何获得反汇编语言,在visual studio 2008中,在debug环境下,在debug/windows/disassembly中可以查看反汇编之后的语言.现在我们看一下阶乘n!的实现 其C语言实现代码如下 [cpp] view plaincopy #include <stdio.h> int factorial(int n); int main(void) { int fact; fact = factorial(4)

C#=&gt;递归反转栈

原理,递归反转栈 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 6 namespace StacksReverse 7 { 8 public class Program 9 { 10 public void Main(string[] args) 11 { 12 StacksReverses stacksReverses = ne

关于复合索引中的2个索引列谁在前谁在后的进一步讨论--实践篇

关于复合索引中的2个索引列谁在前谁在后的进一步讨论--实践篇: 上一次在长老的QQ群里边说了这么一个例子: create table test_pk( id varchar2(10), create_dt date); alter table test_pk modify (id varchar2 (30 )); insert into test_pk select object_id, sysdate from dba_objects; commit 需要执行的查询是: select * fr

39.递归颠倒栈

http://zhedahht.blog.163.com/blog/static/25411174200943182411790/ 分析:乍一看到这道题目,第一反应是把栈里的所有元素逐一pop出来,放到一个数组里,然后在数组里颠倒所有元素,最后把数组中的所有元素逐一push进入栈.这时栈也就颠倒过来了.颠倒一个数组是一件很容易的事情.不过这种思路需要显示分配一个长度为O(n)的数组,而且也没有充分利用递归的特性. 我们再来考虑怎么递归.我们把栈{1, 2, 3, 4, 5}看成由两部分组成:栈顶

1.2 中国象棋将帅问题进一步讨论与扩展:如何用1个变量实现N重循环?[chinese chess]

[题目] 假设在中国象棋中只剩下将帅两个棋子,国人都知道基本规则:将帅不能出九宫格,只能上下左右移动,不能斜向移动,同时将帅不能照面.问在这样条件下,所有可能将帅位置.要求在代码中只能使用一个字节存储变量. [分析] 3种方案: 1)位运算实现1个byte存入和读取2个变量. 2)使用位域把几个不同的对象用一个字节的二进制位域来表示.比如 C++ Code 12345   struct {     unsigned char a: 4;     unsigned char b: 4; } i;

Python旅途——函数的递归和栈的使用

Python--函数之递归.栈的使用 今天主要和大家分享函数的递归,同时引入一个新的概念--栈 1.递归 1.定义 函数的递归指的就是函数自己调用自己,什么是函数自己调用自己呢?我们来看一个栗子: 这里给大家一个数学中的一个数列:斐波那契数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,它指的是前两项的和等于第三项,那么我们对于这

打印完整的递归调用栈

之前在写0-1背包问题的递归解法时,想要弄出完整的递归栈.尝试了使用debug工具手工追踪并画出调用栈,发现太麻烦了,又试了一下使用visual studio的code map功能,发现对于递归,它只会显示递归函数不断调用自己,并不会自动展开成为树的形式.所以我就使用了最简陋的办法,就是自己写了一个类,依赖C++的constructor和destructor来自动将栈输入到一个vector中,并且在main函数结束的地方添加一个语句将其内容输出到文件中. 这里使用了一些C++11的特性,我使用m

【算法题】03-使用递归和栈逆序一个栈

题目 仅用递归操作和栈逆序 一个栈. 一个栈依次压入1.2.3.4.5 那么从栈顶到栈底分别是5,4,3,2,1.将这个栈转置后,从栈顶到栈底为1.2.3.4.5,也就是实现栈中元素的逆序,但是只能用递归来实现 ,不能用其他数据结构. 思路 需要两个递归函数 递归函数一:将栈stack的栈底元素返回并移除 /** * @Desc 获取栈底元素返回并移除 * @param stack * @return */ public static int getAndRemoveLastElement(St

卡尔曼滤波(Kalman Filter) 的进一步讨论

我们在上一篇文章中通过一个简单的例子算是入门卡尔曼滤波了,本文将以此为基础讨论一些技术细节. 卡尔曼滤波(Kalman Filter) http://blog.csdn.net/baimafujinji/article/details/50646814 在上一篇文章中,我们已经对HMM和卡尔曼滤波的关联性进行了初步的讨论.参考文献[3]中将二者之间的关系归结为下表. 上表是什么意思呢?我们其实可以下面的式子来表示,其中,w 和 v 分别表示状态转移 和 测量 过程中的不确定性,也即是噪声,既然是