数据结构C++实现——递归(斐波那契序列、汉诺塔、阶乘)

递归简介

定义

在定义一个过程或函数时,出现直接或者间接调用自己的成分,称为递归。
简而言之,递归就是函数自己调用自己,其过程与一个函数调用其他函数相同。

分类

直接递归:直接调用自己, 形如

void f(){
	...
	f();
	...
}

间接递归:间接调用自己, 形如

void g(){
	...
	f();
	...
}

void f(){
	...
	g();
	...
}

递归实现原理

递归用的是分而治之思想,即将大问题不断分解为小问题,如果小问题还不能解决,再将小问题分解为更小的问题,直到小问题能够解决。

递归函数基本由两部分组成:
递归出口:递归函数终止条件。如果没有递归出口,将导致程序进入死循环,直至栈满溢出。
递归体:确定递归求解时的递推关系。

任何递归函数都可分为两个过程:
分解:将大问题逐渐拆分为小问题。
求解:由小问题往回求解大问题。

以 n!举例,f(n)的功能为求n的阶乘,代码如下

int f(const int n) {
	if (1 == n)
		return 1;
	return n * f(n - 1);
}

在这里插入图片描述
分解:
大问题:求解 n !
拆为:n * (n - 1)!
继续拆: n * (n - 1) * (n - 2) !

直到: 1!
此时可以知道1! = 1, n == 1即为递归出口

求值:
由1!可求得2!
由2! 可求得 3!

由此可求得n!
n!就得到了解决

实例

阶乘

#include<iostream>

using namespace std;

int f(const int n) {
	if (1 == n)
		return 1;
	return n * f(n - 1);
}

int main()
{
	cout << f(5) << endl;
	return 0;
}

输出结果为
120

累加

#include<iostream>

using namespace std;

int f(const int n) {
	if (1 == n)
		return 1;
	return n + f(n - 1);
}

int main()
{
	cout << f(100) << endl;
	return 0;
}

函数体大致与阶乘相同,输出结果为 5050

Fibonacci数列

Fibonacci数列介绍

1, 1,2,3, 5,8,13,21, 34,55,...

即函数第n位上的数始终是第n-1和n-2位置上的数的和,第1位和第2位为1

求Fibonacci数列第n位上的值
迭代实现

#include<iostream>
using namespace std;
int Fibo_Iter(const int n) {
	if (1 == n || 2 == n)
		return 1;

	int n1 = 1, n2 = 1, n3;
	for (int i = 3; i <= n; ++i) {
		n3 = n1 + n2;
		n1 = n2;
		n2 = n3;
	}
	return n2;
}

int main()
{
	for (int i = 1; i != 10; ++i)
		cout << Fibo_Iter(i) << " ";
	return 0;
}

输出为
在这里插入图片描述
递归实现

#include<iostream>
using namespace std;
int Fibo_Recur(const int n) {
	if (1 == n || 2 == n)
		return 1;

	return Fibo_Recur(n - 1) + Fibo_Recur(n - 2);
}

int main()
{
	for (int i = 1; i != 10; ++i)
		cout << Fibo_Iter(i) << " ";
	return 0;
}

输出结果与迭代结果一致

汉诺塔

规则:将A柱上的圆盘通过B柱移动在C柱,一次只能移动一个圆盘,且大圆盘不能放在小圆盘上面

在这里插入图片描述
汉诺塔游戏链接

分析

假设A柱上一共有n个圆盘,问题可以拆解为

  1. 将A上的n-1个圆盘通过C移动到B上
  2. 再将A上第n个(最下面那个)圆盘移动到C上

此时再将上面第1步分解为

  1. 将B上的n-2个圆盘通过C移动到A上
  2. 将B上的第n-1个圆盘移动到C上

再分解上面第1步

到最后一步时,只剩1个圆盘在A或者B上,此时将这最后一个圆盘移动到C上,即完成了n个圆盘从A移动到C的任务。

代码如下

#include<iostream>

using namespace std;

void Hanoi(const int n, const char A, const char B, const char C)
//将A上的n个圆盘,通过B,移动到C
{
	if (1 == n)	//函数出口
	{
		cout << A << "->" << C << endl;
		return;
	}
	Hanoi(n - 1, A, C, B);	//将A上n-1个圆盘通过C移动到B
	cout << A << "->" << C << endl;		//将A上第n个圆盘移动到C
	Hanoi(n-1, B, A, C);	//将B上n-1个圆盘通过A移动到C
}

int main()
{
	Hanoi(3, 'X', 'Y', 'Z');	//3表示最初A上有3个圆盘,可以自行更改
	return 0;
}

运行结果如下
在这里插入图片描述

递归的优缺点

优点

递归算法使算法代码更加简洁易懂,在某些情况下,效果尤为明显,如树的遍历、汉诺塔游戏、走迷宫

缺点

  1. 由于递归是通过不断地弹栈和压栈实现的,每一次压栈都会进行参数拷贝、空间分配等过程,因此会耗费大量的时间和空间,一次调用结束会进行弹栈,也需要耗费时间。
  2. 递归出口设计不合理会导致调用栈溢出

在程序中选用递归算法还是迭代算法需要考虑到递归的深度,在递归次数大于一定值时(我的电脑上为20),递归的效率的非常低了。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值