【算法】详解递归算法

程序调用自身的编程技巧称为递归( recursion)。

简单来说,循环是有去无回,而递归则是有去有回(因为存在终止条件)。
在这里插入图片描述
递归的能力在于用有限的语句来定义对象的无限集合。

在这里插入图片描述
构成递归需具备的条件:

  1. 子问题须与原始问题为同样的事,且更为简单;

2 不能无限制地调用本身,须有个出口,化简为非递归状况处理。

某人的双亲是他的祖先(基本情况)。某人祖先的双亲同样是某人的祖先(递归步骤)。斐波纳契数列(Fibonacci Sequence),又称黄金

分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21…

斐波那契弧线,也称为斐波那契扇形线。

在这里插入图片描述

斐波那契数列中的斐波那契数会经常出现在我们的眼前——比如松果、凤梨、树叶的排列、某些花朵的花瓣数(典型的有向日葵花瓣),蜂

巢,蜻蜓翅膀,超越数e(可以推出更多),黄金矩形、黄金分割、等角螺线,十二平均律等。

def fib(n):
        a, b = 1, 1
        while a < n:
                print(a, end=' ')
                a, b = b, a+b
fib(100) #输出的是100以内的斐波那契数列
 
 
i = 1
j = 1
print(i,\n,j)
for x in range(2,100):
    if x == i + j:
        print(x)
        j = i
        i = x

递归算法一般用于解决三类问题:

(1)数据的定义是按递归定义的。(Fibonacci函数)

(2)问题解法按递归算法实现。

这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。

(3)数据的结构形式是按递归定义的。

如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。

递归的缺点:

递归算法解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归

更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。

递归工作栈
IA-32使用栈来支持过程的嵌套调用。每个过程都有自己的栈区,称为栈帧(stack frame) 。因此,一个栈由若干栈帧组成,每个栈帧用专

门的帧指针寄存器EBP指定起始位置,当前栈帧的范围在其和栈指针寄存器ESP指向区域之间。IA-32规定,寄存器EAX、ECX和EDX是调

用者保存寄存器。当过程P调用过程Q时,Q 可以直接使用这三个寄存器,不用将它们的值保存到栈中,这也意味着,如果P在从Q返回后还

要用这三个寄存器的话,P应在转到Q之前先保存它们的值,并在从Q返回后先恢复它们的值再使用。寄存器EBX、ESl、EDI是被调用者保

存寄存器,Q必须先将它们的值保存到栈中再使用它们,并在返回P之前先恢复它们的值。

(1)每次递归调用前,先将参数n~参数1按序复制到调用过程栈帧中

(2)执行call指令:首先将返回地址(call指令要执行时EIP的值,即call指令下一条指令的地址)压入栈顶,然后将程序跳转到当前调用的

方法的起始地址,相当于执行了push和jump指令。

递归调用时,每一层调用过程栈帧中存放的返回地址都是相同的。

(3)每次递归,必定要先push %ebp(把原帧指针保存在栈顶)和mov %esp,%ebp(把存放原帧指针的栈顶,设置为新栈底)

被调用者定义的非静态局部变量仅存放于当前栈帧,调用结束后就被释放了。

最后往往通过EAX寄存器将结果返回给调用者。

(4)执行leave指令:将栈指针指向帧指针,然后pop备份栈顶存放的原帧指针到EBP。

(5)最后执行ret指令:将栈顶的返回地址弹出到EIP,然后按照EIP此时指示的指令继续执行程序。

在这里插入图片描述
过程调用中使用的栈机制和寄存器使用约定,使得可以进行过程的嵌套调用和递归调用。

在这里插入图片描述

import os

def file_display(filepath):
    for each in os.listdir(filepath):
        # 得到文件的绝对路径:
        absolute_path = os.path.join(filepath, each)
        # 得到是否为文件还是目录的布尔值:
        is_file = os.path.isfile(absolute_path)
        if is_file:
            # 当前的绝对路径为文件:
            print(each)
        else:
            # 当前的绝对路径为目录:
            file_display(absolute_path)

file_display('/home/pushy')

递归典型问题: 梵塔问题(汉诺塔问题)

已知有三根针分别用A, B, C表示,在A中从上到下依次放n个从小到大的盘子,现要求把所有的盘子从A针全部移到B针,移动规则是:可以

使用C临时存放盘子,每次只能移动一块盘子,而且每根针上不能出现大盘压小盘,找出移动次数最小的方案。

/*Name:HANOITOWER
*Description:solvethehanoitowerproblembyrecursion
*/
#include<stdio.h>
#include<stdlib.h>
/*movenplates:from-->to,
*thebuffercanbeusedifneeded*/
inthanoi(intn,charfrom,charbuffer,charto)
{
    if(n==1)
    {
        /*movetheNO.1platedirectly:from-->to*/
        printf("Moveplate#%dfrom%cto%c\n",n,from,to);
        /*theNO.1plateismovedsoreturn*/
        return0;
    }
    else
    {
        /*nplatestobemoved:from-->to,
        *movethen-1platesabove:from-->buffer,
        *givethistasktothenextrecursion*/
        hanoi(n-1,from,to,buffer);
        /*then-1platesaboveweremovedtobuffer,
        *sotheNO.nplatecanbemoveddirectly*/
        printf("Moveplate#%dfrom%cto%c\n",n,from,to);
        /*howeverthen-1platesarestillinbuffer,
        *movethemtotheterminalposition,
        *(the"from"positionhasnoplate,&canbeoneso-calledbuffer)*/
        hanoi(n-1,buffer,from,to);
        /*thetaskgivenisdonesoreturn*/
        return0;
    }
}
intmain()
{
#defineN4
    /*NplatesinA,let'smovethemtoC*/
    hanoi(N,'A','B','C');
    return0;
}

微信公众号“计算机基础学”关注我哟

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值