递归的几种类型

本文详细介绍了递归在几种经典问题中的应用,包括阶乘、斐波那契数列、倒序输出正整数、汉诺塔问题以及排列问题。通过递归思想,解释了如何解决这些数学和逻辑难题,提供了解决方案的递归表达式和部分实现思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文:https://blog.youkuaiyun.com/qq_34039315/article/details/78679029

1、阶乘

递归思想:n! = n * (n-1)! (直接看公式吧)

首先分析数列的递归表达式: 

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

2、斐波那契数列

斐波那契数列的排列是:0,1,1,2,3,5,8,13,21,34,55,89,144……依次类推下去,你会发现,它后一个数等于前面两个数的和。在这个数列中的数字,就被称为斐波那契数。

递归思想:一个数等于前两个数的和。(这并不是废话,这是执行思路)

首先分析数列的递归表达式: 

 

int f(n)
{
	if(n<=1) return 1;
	else return f(n-1)+f(n-2);
}

3、倒序输出一个正整数

例如给出正整数 n=12345,希望以各位数的逆序形式输出,即输出54321。

递归思想:首先输出这个数的个位数,然后再输出前面数字的个位数,直到之前没数字。

 首先分析数列的递归表达式:

写法一:

#include<iostream>
using namespace std;

void f(int n)
{
	if(n/10==0) cout<<n;
	else
	{
		cout<<n%10;
		f(n/10);			
	}	
}

int main()
{
	f(12345);
	return 0;	
}

写法二: 

#include<iostream>
using namespace std;
void f(int n)
{
	cout<<n%10;
	if(n/10)
	{
		f(n/10);
	}
}

int main()
{
	f(12345);
	return 0;
}

 

 

4、汉诺塔问题

有n个盘子,3个柱子x,y,z。

1、欲将x柱子上的n个盘子,全部移动到z柱子上;
2、大盘子不能压到小盘子上面。

递归思想: 
1. 将x柱子上的n-1个盘子都移到空闲的y柱子上,并且满足上面的所有条件;
2. 将x杆上的第n个盘子移到z柱子上;
3. 剩下问题就是将y柱子上的n-1个盘子移动到z柱子上了。

公式描述有点麻烦,用语言描述下吧: 
1. 以z柱子为中介,将前n-1个盘子从x柱子挪到y柱子上(本身就是一个n-1的汉诺塔问题了!) ;
2. 将第n个圆盘移动到z柱子上; 
3. 以x柱子为中介,将y柱子上的n-1个盘子移到z柱子上(本身就是一个n-1的汉诺塔问题了!)。
 

#include<iostream>
using namespace std;

void hanoi(int n,char x,char y,char z) //从x柱子,借助y柱子,移动到z柱子 
{
	if(n==0) return;
	else
	{
		hanoi(n-1,x,z,y);   //分治思想,将n-1个盘子看作一个整体
		cout<<"将"<<n<<"号盘子,"<<"从"<<x<<"柱子,移动到"<<z<<"柱子"<<endl;
		hanoi(n-1,y,x,z);		
	}		
} 

int main()
{
	hanoi(5,'A','B','C');
	
	return 0;	
} 

参考:https://blog.youkuaiyun.com/qq_41705423/article/details/82025409 

 

5、排列问题

https://blog.youkuaiyun.com/wzy_1988/article/details/8939140

https://blog.youkuaiyun.com/Hackbuteer1/article/details/7462447

输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

递归思想: 
假如针对abc的排列,可以分成 (1)、以a开头,加上bc的排列; (2)、以b开头,加上ac的排列; (3)、以c开头,加上ab的排列。

#include<iostream>
using namespace std;

void permutation(int a[],int k,int n) //k:当前欲固定第k号数据;n:一共有n个数据 
{
	if(k==n-1)  //递归结束条件:选取到第n-1号数据(最后一个数据) 
	{
		for(int i=0;i<n;i++)
		{
			cout<<a[i];
		}
		cout<<endl;
	}
	else
	{
		for(int i=k;i<n;i++)  //轮流当第k号元素 
		{
			swap(a[i],a[k]);  //经过这一步,第k号数据固定下来了 
			permutation(a,k+1,n);  //然后处理后面的数据 
			swap(a[i],a[k]);  //保证下一次循环时,依然是最初的次序 
		}		
	}	
} 

int main()
{
	int a[3]={1,2,3};
	permutation(a,0,3);	
	return 0;
} 

【法二】 

算法笔记 P115

#include<iostream>
using namespace std;
const int maxn=11;

int n,P[maxn],hashTable[maxn]={false};

void generateP(int index)
{
	if(index==n+1) //递归结束条件,已经处理完1号~n号元素
	{
		for(int i=1;i<=n;i++) //从1号开始存储元素,一共n个
		{
			cout<<P[i];
		} 
		cout<<endl;
	}
	for(int x=1;x<=n;x++)
	{
		if(hashTable[x]==false) 
		{
			P[index]=x;
			hashTable[x]=true;
			generateP(index+1);
			hashTable[x]=false;
		}
	}
}
int main()
{
	n=3;
	generateP(1);
	return 0;
}

 

### 递归的概念 递归是一种解决问题的方法,其核心思想是将一个问题分解为更小的子问题,并通过重复调用自身来解决这些子问题[^5]。递归通常由两个部分组成:基本情况(Base Case)和递归步骤(Recursive Step)。其中,基本情况用于终止递归过程,而递归步骤则负责逐步缩小问题规模。 #### 类型定义 递归可以分为以下几种类型: 1. **直接递归** 当一个函数在其内部直接调用了它自己,则称为直接递归。这是最常见递归形式。 2. **间接递归** 如果函数 A 调用另一个函数 B,而函数 B 又反过来调用函数 A,则这种递归被称为间接递归。 3. **尾递归** 尾递归是指递归调用作为整个函数体的最后一项操作执行的情况。由于尾递归可以在编译器优化下转换为循环结构,因此它可以有效减少栈空间的消耗。 --- ### 应用场景 递归广泛应用于各种计算机科学领域以及实际工程问题中。以下是几个典型的应用场景及其具体实现: #### 1. 数学运算 许多数学问题是天然适合采用递归来解决的例子之一便是阶乘计算。例如,在 C 或 JavaScript 中可以通过如下代码实现阶乘功能: ```javascript function factorial(n) { if (n === 0 || n === 1) { // 基本情况 return 1; } return n * factorial(n - 1); // 递归步骤 } console.log(factorial(5)); // 输出: 120 ``` 同样地,斐波那契数列也是一个经典的递归例子[^4]: ```c int fibonacci(int n) { if (n == 0) return 0; // 基本情况 if (n == 1) return 1; // 基本情况 return fibonacci(n - 1) + fibonacci(n - 2); // 递归步骤 } ``` #### 2. 数据结构遍历 对于树形数据结构而言,递归是非常自然的选择。比如二叉树的前序、中序和后序遍历都可以轻松利用递归完成。下面展示了一个简单的二叉树前序遍历算法: ```cpp void preorderTraversal(TreeNode* root) { if (!root) return; // 终止条件 cout << root->val << " "; // 访问根节点 preorderTraversal(root->left); // 左子树递归访问 preorderTraversal(root->right); // 右子树递归访问 } ``` #### 3. 文件系统目录遍历 在操作系统层面,文件夹层次结构本质上也是一种树状模型,因此可以用递归来实现对目录及其子目录的内容扫描[^1]。 #### 4. 动态规划与分治法 某些复杂的动态规划问题也可以借助递归的思想进行求解。汉诺塔就是一个很好的实例——该问题不仅体现了递归逻辑之美,同时也展示了如何通过分治策略简化难题。 --- ### 总结 尽管递归能够使程序设计更加简洁优雅,但也需要注意性能开销等问题。特别是在处理大规模输入时,可能会因为过深的嵌套而导致堆栈溢出错误。此时可考虑改写成非递归版本或者引入其他技术手段加以改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值