递归:斐波那契数列、阶乘、求字符串长度、n的k次方的分析(多图预警)

首先第一步,我们得认识什么是递归:

递归的思想:大事化小

递归的两个必要条件

1、存在限制条件,满足条件后停止递归

2、每次递归调用之后,越来越接近限制条件

具有以下特征的问题可考虑递归求解:

  • 问题具有递推关系,比如杨辉三角、计算阶乘。
  • 反向性问题,比如取反。

 这里给了一个按位数输出数字的递归程序,大家可以看图先理解一下递归。

其实我们生活中就有递归的实例:查字典。

当你查一个词,发现这个词的解释中的一个词还是不懂,这个词也需要查字典,但是,让人觉得无奈的是,第二个词里还是有不懂的词,但是我们比较有毅力,遇到艰难险阻不放弃,你继续查找这个不懂的词......这样查下去,直到最终的词的解释你看懂了,那么查字典也就结束了,然后你开始原路返回,逐个击破之前查过的每一个词,最终,你明白了最开始那个词的意思。

这就是大事化小的典型例子,也是递归的生活具体实例,那么现在,大家应该已经认识递归这个东西了。所以我们现在就来看一看我们今天要解决的几个问题吧:

1、斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=1,F(1)=1, F(n)=F(n - 1)+F(n - 2)

那么,他究竟有多么有名?美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。可见斐波那契数列的重要性,那么我们今天就来探讨一下斐波那契数列的数字的计算问题。

上面已经有了关于斐波那契数列的计算式,下图是概要剖析,我们也是基于同样的算法把这个程序书写出来:

int count = 0;
long Fib(int n)
{
	if (n >= 2)
	{
		count++;
		return Fib(n - 1) + Fib(n - 2);
	}
	else
		return 1;
}

(在这里偷偷给出计算时间的函数、使用的时候记得写上头文件time.h)

    clock_t begin, end;
	double cost;
	int n;
	scanf("%d", &n);
	begin = clock();
	long ret = Fib(n);
	end = clock();
	cost = (double)(end - begin) / CLOCKS_PER_SEC;
	printf("The result is %ld.\n", ret);
	printf("The time cost is %lfsecs\n", cost);
	printf("The times is %d.\n", count);

 但是我们已经意识到这样计算的时间复杂度十分巨大,几乎是不可忽视了,所以我们需要更好的方法,于是,我们提出了使用循环的方法进行计算:

long fib(int n)
{
	int a = 1;
	int b = 1;
	int c;
	while (n)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
		count++;
	}
	return c;
}

那么,这些代码其实也很简单,至于递归的过程,也跟我们第一张图是一样的,我们就不多做赘述了。

2、阶乘问题

阶乘是基斯顿·卡曼(Christian Kramp,1760~1826)于 1808 年发明的运算符号。

一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。自然数n的阶乘写作n!。1808年,基斯顿·卡曼引进这个表示法。

亦即n!=1×2×3×...×(n-1)×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n

那么我们把这些概要剖析一下,如下图:

int Fact(int n)
{
	if (n <= 1)
		return 1;
	else
		return n * Fact(n - 1);
}

也就是说,我们每次都需要把阶乘的数字减小,一步一步的缩小我们的问题,由5的阶乘到4的阶乘,最后到1,再一步一步的返回,最终我们就能得到我们的结果了(联想查字典的实例)

那么阶乘的问题其实已经解决了,阶乘也可以使用循环的方式,由于循环的方法很简单,所以在这里就不给出了(一个for循环里限制n--,取一个临时变量来每次乘n)

3、字符串求长的问题,关于字符串求长,其实头文件string.h里是有包含的,这里我们等于使用我们所理解的方式重现一下这个方法。那么我们当然可以一段循环代码搞定(如下图)

int my_strlen(char* str)
{
	int count = 0;
	while (*str != '\0')//判断此次指针指向的数据是不是结束标志
	{
		count++;//每次统计计数器加一
		str++;//每次统计之后,指针后移
	}
	return count;
}

不过我们今天要看的是递归,所以我们再使用递归的方式来看看:

int my_strlen2(char* str)
{
	if (*str != '\0')
		return 1 + my_strlen2(str + 1);
	else
		return 0;
}

 注意这里的 return 1+ my_strlen2(str+1) ,这里保证了每次指针向后移动一个长度,并且保证了计数器的正常工作。(这里的+1就体现的很巧妙,每次都保证我在计数,而不是定义全局变量那样)

4、n的k次方的问题,求次方,主要是科学计算,这里我们就当成一个简单的问题来处理:

//计算n的k次方
#include<stdio.h>
long cacul(int n, int k)
{
	if (k >= 1)
	{
		k--;
		return n * cacul(n, k);
	}
	else
		return 1;
}
void main()
{
	int n, k;
	printf("Please enter n and k:\n");
	scanf("%d%d", &n, &k);
	long ret = cacul(n, k);
	printf("the result is %ld.\n", ret);
}

 这里我们就不画图解释了,因为前面的图和生活实例应该已经让大家明白了递归的工作原理。

此处我们要注意,当次方k=0时,我们需要返回一个1,不然程序也不知道该返回什么(毕竟我们使用了k--,就需要考虑这些情况),而这个求次方,程序跟求阶乘是几乎一样的,都需要返回一个 n*函数名 (这是计算要求,我们需要具体问题具体分析)

那么今天的递归,就分享到这里了,希望能帮到此前没有理解递归的朋友。

  • 33
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值