递归分为:不动点组合子,尾部递归还有递归数据,目前 我只研究到尾部递归
再了解递归前我们先了解一下什么是斐波那契数列?
所谓Fibonacci数列是指这样一种数列,它的前两项均为1,从第三项开始各项均为前两项之和。用数学公式表示出来就是:
1 (n=1,2)
fib(n)=
fib(n-1)+fib(n-2) (n>2)
例如:
有这样一组数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...
特别指出:第0项是0,第1项是第一个1。
这个数列从第二项开始,每一项都等于前两项之和。
所以它的公式应该是:a1=1,a2=2,an=a(n-1)+a(n-2)(n>=3)
其它信息各位可以百度看一下
http://baike.baidu.com/link?url=F-D2_4R1w5gCz5uFcR2drVZAMFQm2g22HYyXkj01u-5DFRrh9owiOdtr982VyreQ
尾递归(tail recursive),看名字就知道是某种形式的递归。简单的说递归就是函数自己调用自己。那尾递归和递归之间的差别就只能体现在参数上了。
维基百简直是如下解释的:
尾部递归[编辑]
尾部递归是指递归函数在调用自身后直接传回其值,而不对其再加运算。尾部递归与循环是等价的,而且在一些语言(如Scheme中)可以被优化为循环指令。 因此,在这些语言中尾部递归不会占用调用堆栈空间。以下Scheme程序同样计算一个数字的阶乘,但是使用尾部递归:[4]
尾递归解释如下:
尾部递归是一种编程技巧。递归函数是指一些会在函数内调用自己的函数,如果在递归函数中,递归调用返回的结果总被直接返回,则称为尾部递归。尾部递归的函数有助将算法转化成函数编程语言,而且从编译器角度来说,亦容易优化成为普通循环。这是因为从电脑的基本面来说,所有的循环都是利用重复移跳到代码的开头来实现的。如果有尾部归递,就只需要叠套一个堆栈,因为电脑只需要将函数的参数改变再重新调用一次。利用尾部递归最主要的目的是要优化,例如在Scheme语言中,明确规定必须针对尾部递归作优化。可见尾部递归的作用,是非常依赖于具体实现的。
我们还是从简单的斐波那契开始了解尾递归吧。
用普通的递归计算Fibonacci数列:
#include "stdio.h" #include "math.h" int factorial(int n); int main(void) { int i, n, rs; printf("请输入斐波那契数n:"); scanf("%d",&n); rs = factorial(n); printf("%d \n", rs); return 0; } // 递归 int factorial(int n) { if(n <= 2) { return 1; } else { return factorial(n-1) + factorial(n-2); } }
程序员运行结果如下:
请输入斐波那契数n:20 6765 Process returned 0 (0x0) execution time : 3.502 s Press any key to continue.
下面我们看看如何用尾递归实现斐波那契数。
#include "stdio.h" #include "math.h" int factorial(int n); int main(void) { int i, n, rs; printf("请输入斐波那契数n:"); scanf("%d",&n); rs = factorial_tail(n, 1, 1); printf("%d ", rs); return 0; } int factorial_tail(int n,int acc1,int acc2) { if (n < 2) { return acc1; } else { return factorial_tail(n-1,acc2,acc1+acc2); } }
程序员运行结果如下:
请输入斐波那契数n:20 6765 Process returned 0 (0x0) execution time : 1.460 s Press any key to continue.
快了一倍有多。当然这是不完全统计,有兴趣的话可以自行计算大规模的值,这里只是介绍尾递归而已。
我们可以打印一下程序的执行过程,函数加入下面的打印语句:
int factorial_tail(int n,int acc1,int acc2) { if (n < 2) { return acc1; } else { printf("factorial_tail(%d, %d, %d) \n",n-1,acc2,acc1+acc2); return factorial_tail(n-1,acc2,acc1+acc2); } }
程序运行结果:
请输入斐波那契数n:10 factorial_tail(9, 1, 2) factorial_tail(8, 2, 3) factorial_tail(7, 3, 5) factorial_tail(6, 5, 8) factorial_tail(5, 8, 13) factorial_tail(4, 13, 21) factorial_tail(3, 21, 34) factorial_tail(2, 34, 55) factorial_tail(1, 55, 89) 55 Process returned 0 (0x0) execution time : 1.393 s Press any key to continue.
从上面的调试就可以很清晰地看出尾递归的计算过程了。acc1就是第n个数,而acc2就是第n与第n+1个数的和,这就是我们前面讲到的“迭代”的精髓,计算结果参与到下一次的计算,从而减少很多重复计算量。
fibonacci(n-1,acc2,acc1+acc2)将原本朴素的递归产生的栈的层次像二叉树一样,以指数级增长,但是现在栈的层次却像是数组,变成线性增长了,实在是奇妙,总结起来也很简单,原本栈是先扩展开,然后边收拢边计算结果,现在却变成在调用自身的同时通过参数来计算。
相关推荐
尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者...
2. **补全数列**:如果待查找数列长度小于斐波那契数列中的某一项,则将数列尾部的值复制到剩余的位置,确保数列长度等于斐波那契数列的该项值。 3. **查找过程**:从斐波那契数列的倒数第二项开始,逐步缩小查找...
尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者...
斐波那契数列的递归定义为`Fib(n) = Fib(n-1) + Fib(n-2)`,其中`Fib(0)`和`Fib(1)`为基本项。相应的递归算法如下: ```cpp long Fib(long n) { if (n ) return n; else return Fib(n-1) + Fib(n-2); } ``` 这种...
- **数学问题**:阶乘计算、斐波那契数列求解等。 ### 二、递归在树结构中的应用 #### 2.1 树的基本概念 - **树**是由节点组成的数据结构,其中一个节点作为根节点,其他节点分为多个不相交的子树。 - **二叉树**...
- 第三题中,`fun`函数用于字符串逆置,使用了两个指针,一个从字符串头部开始,另一个从尾部开始,交换它们指向的字符,直到两个指针相遇,实现了字符串的反转。 4. **数列求和**: - 第四题要求找到所有能被3或...
2. 斐波那契数列(Fibonacci Sequence): 斐波那契数列是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, ...,后面的每一个数都是前两个数的和。这个序列在自然界和计算机科学中都有广泛应用,如黄金分割比例和优化问题...
解决方案:可以使用双指针技术,一个指针从字符串头部开始,另一个指针从字符串尾部开始,比较字符是否相等。如果相等,则继续比较,否则,字符串不是回文。 知识点三:素数计算 如何判断一个数是否为素数?如何...
2. **斐波那契数列**:斐波那契数列的第n项可以通过递归或迭代计算。在提供的代码中,当n小于等于2时,直接返回1,否则使用循环计算前两项的和作为下一项。递归思想在这类问题中尤为关键。 3. **爱因斯坦阶梯问题**...
尾递归是尾调用的一种特殊形式,它发生在函数在其尾部递归调用自身。比如,我们可以使用尾递归优化来解决斐波那契数列的问题: ```javascript // 非尾递归 function fibonacci(n) { if (n === 0) return 0; if (n...
递归程序结构的基本模式包括直接递归和间接递归,它们在处理如斐波那契数列、阶乘计算等问题时尤其有用。 数据结构是任何编程语言的基础,LISP中的数据结构主要是列表,但也可以创建更复杂的数据结构,如树和图。...
2. 斐波那契数列:斐波那契数列的定义是`F(n) = F(n-1) + F(n-2)`,可以通过递归或动态规划来实现。 3. 字符串逆置:使用两个指针,一个从头开始,一个从尾部开始,交换它们指向的字符,直到相遇。代码中`(s+i)`和`...
- **生成方式**:从0和1开始,逐步计算后续的数值。 - **时间复杂度**:O(n),因为每个数字仅计算一次。 - **空间复杂度**:O(n),用于存储整个数列。 #### 四、归并排序(Merge Sort) 归并排序是一种基于分治...
在实际代码中,`struct node`的定义和相关的函数未给出,但通常会涉及从尾部开始遍历链表,逐个打印节点。 5. **单链表检索(学生学号)**: 要实现这个功能,需要编写一个函数,接收学号作为参数,然后遍历链表,...
递归公式如"斐波那契数列"的计算:f(n) = f(n-1) + f(n-2),或二分查找的时间复杂度:log2(n)。 最后,时间复杂度的计算涉及高次幂和求和公式,例如等差数列和等比数列的求和。这些数学工具对于理解和优化算法至关...
O(logn)求Fibonacci数列 - **矩阵快速幂法**:利用矩阵快速幂的思想来求解斐波那契数列。 - **核心思想**:将斐波那契数列的递推关系转化为矩阵的形式,然后利用矩阵快速幂的方法求解。 - **代码实现**:展示如何...
1. **递归算法**:在题目18中,函数`fun`使用递归方法计算斐波那契数列的第n项。斐波那契数列是每个数等于前两个数之和,通常递归公式为`fib(n) = fib(n-1) + fib(n-2)`,基础情况为`fib(1) = 1`和`fib(2) = 1`。...
7) **斐波那契数列**:递归计算斐波那契数列。 8) **汉诺塔**:解决汉诺塔问题。 9) **杨辉三角**:生成杨辉三角形。 **2.4 队列** - **概述**:队列是一种先进先出(FIFO)的数据结构。 - **链表实现**:使用链表...
通过双指针技术,一个从字符串首部开始,一个从尾部开始,比较对应位置的字符是否相等,如果发现不相等则说明不是回文字符串。 9. **寻找回文子串**: 如果寻找一个字符串是否包含回文子串,可以使用滑动窗口或者...