文章目录
一、何为递归算法?
用一句话简单概括:直接或者间接地调用自身的算法称为递归算法。
在此之前,我们或多或少的接触到斐波那契数列,阶乘等等,这些问题有一定规律,但是如果把问题扩大规模,对我来说无法思考清晰,这篇博客是对自己的递归认知的进一步思考和探索。
二、例题展示
再回头看这些递归算法,其递推公式和起点便是关键。
【例题2-1】阶乘函数
阶乘函数可以被定义为:
从这个公式可以观察到如下信息:自变量n的定义域是非负整数;而且公式给出了这个函数的初始值。每个递归函数都必须有定义的初始值,否则将无法计算。此时我们可以相应递归函数:
int factorial(int n){
if(n == 0)
return 1;
return n*factorial(n - 1);
}
这可能是我们第一次遇到递归的举例。
【例题2-2】斐波那契数列
斐波那契数列可以递归地定义为
这一个递归模型是由两个初始值和一个递归方程确定的,所以我们可以写出相应函数代码
int fionacci(int n){
if(n <= 1)
return 1;
return fibonacci(n - 1) + fibonacci(n -2);
}
【例题2-3】Ackerman函数
在学习这个递归模型时,要明白这不是普通的递归模型,这是特殊的双递归函数。
双递归函数:当一个函数及它的一个变量是由函数本身定义时,称这个函数是双递归函数。
现在给出Ackerman函数的定义:
下面给出我自己的代码理解:
int A(int m , int n){
if(m == 0) return n+1;
else
{
if(n == 0) {
return A(m - 1 , 1);
}
else
{
return A(m-1 , A(m,n-1));
}
}
}
这只是一个外套,如果真的涉及到内部实际规律或者说让我们找出某题的某个实际,我们需要多耗费更多脑细胞。
【例题2-4】排序问题
原题目有些麻烦,稍稍化简一下:在一个数组中,拿出相应元素进行递归排序(没有要求)。这里给出书中所写代码(以一个力扣答题代码形式):
template<class Type>
void Perm(Type list[] , int k , int m){
if(k == m)
{
for(int i=0 ; i <= m ; i++)
{
cout<<i;
}
cout<<endl;
}
//输出[0,m]之间的元素
else
{
//交换顺序 第一个循环是确定第一个元素
for(int i = k; i <= m; i++)
{
Swep(list[k],list[i]);
Perm(list, k+1,m);
//接着交换剩下的元素 依次确定剩下元素的文字 最后输出
Swep(list[k],list[i]);
//回到最初的状态
}
}
}
template<class Type>
inline void Swep(Type &a,Type &b){
Type temp=a;
a=b;
b=temp;
}
在我的舍友开导下,这是一个涵盖数组内大部分排序的总体函数,当 k=0, m=n-1时,此函数便满足我们的题目要求。虽然这个函数无法排除存在元素相同的情况,但是也令我思索良久。这里使用了一个inline前缀修饰符:
inline修饰符:表示为内联函数,实质为”调用函数的主体取代调用动作“,避免了频繁调用函数对栈内存重复开辟所带来的消耗。
三、分治法的基本思想
将一个规模为n的问题分解为k个规模较小的子问题。
从分治法的一般设计过程,用它设计出的算法一般是递归算法,而此时要考虑的是相应算法的时间复杂度,下面为一个算法的T(n)举例:
然后经递推,得到相应的含参公式,经过对参数的替换使其满足为只含n的表达式,最后得到相应的时间复杂度。
递归算法详解:例题演示与分治法应用
2972





