算法学习2:初识递归

递归算法详解:例题演示与分治法应用

文章目录


一、何为递归算法? 

用一句话简单概括:直接或者间接地调用自身的算法称为递归算法。

在此之前,我们或多或少的接触到斐波那契数列,阶乘等等,这些问题有一定规律,但是如果把问题扩大规模,对我来说无法思考清晰,这篇博客是对自己的递归认知的进一步思考和探索。


二、例题展示

      再回头看这些递归算法,其递推公式和起点便是关键。

【例题2-1】阶乘函数

      阶乘函数可以被定义为:

      n!=\begin{cases} 1 & \text{ } n=0 \\ n(n-1)!& \text{ } n>0 \end{cases}

     从这个公式可以观察到如下信息:自变量n的定义域是非负整数;而且公式给出了这个函数的初始值。每个递归函数都必须有定义的初始值,否则将无法计算。此时我们可以相应递归函数:

int factorial(int n){
    if(n == 0)
      return 1;
    return n*factorial(n - 1);
}

     这可能是我们第一次遇到递归的举例。


【例题2-2】斐波那契数列

    斐波那契数列可以递归地定义为

         F(n)=\begin{cases} 1 & \text{ } n=0 \\ 1 & \text{ } n=1 \\ F(n-1)+F(n-2)& \text{ } n>1 \end{cases}

    这一个递归模型是由两个初始值和一个递归方程确定的,所以我们可以写出相应函数代码

int fionacci(int n){
    if(n <= 1)
      return 1;
    return fibonacci(n - 1) + fibonacci(n -2);
}

【例题2-3】Ackerman函数

    在学习这个递归模型时,要明白这不是普通的递归模型,这是特殊的双递归函数。

双递归函数:当一个函数及它的一个变量是由函数本身定义时,称这个函数是双递归函数。

    现在给出Ackerman函数的定义:

  A(m,n)=\begin{cases} n+1 & \text{ } m= 0\\ A(m-1 , 1)& \text{ } m>0 ,n=0 \\ A(m-1,A(m,n-1))& \text{ } m>0 ,n>0 \end{cases} 

    下面给出我自己的代码理解:

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)举例:

T(n)=\begin{cases} T(1) & \text{ } n=1 \\ kT(n/m)+f(n)& \text{ } n>1 \end{cases}

 然后经递推,得到相应的含参公式,经过对参数的替换使其满足为只含n的表达式,最后得到相应的时间复杂度。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值