数据结构与算法(三) - 递归算法

数据结构与算法(三)-递归算法

Recursion,递归是一种常用的算法,也可以称为一种编程技巧。在后续数据结构与算法的编码过程中都要使用到递归,比如DFS深度优先,二叉树前中后序遍历等。

1. 概念

程序调用自身的编程技巧称为递归( recursion),所有的递归问题都可以使用递推公式来表示。构成递归需具备的条件:

  • 子问题须与原始问题为同样的事,且更为简单;
  • 不能无限制地调用本身,须有个出口,化简为非递归状况处理。

下面举一个例子帮助理解递归:

例:儿问其父:吾之十八代祖名何?

其父不明,父问其父

其父不明,父问其父

其父不明,父问其父

… …

晌后,其十八代祖回其子:你猜。转化成代码如下:

public String ancestor(int n){
    if(n == 18) return "你猜";
    return ancestor(n+1);//问父辈
}

2. 满足递归的条件

2.1 递归表达式(规律)

如果一个问题的解能够拆分成多个子问题的解,拆封之后,子问题和该问题在求解上除了数据规模不一样外,求解的思路和该问题的求解思路完全相同,即找到一种规律,也就是递推公式,那么这个问题就可以使用递归来解析。上面的例子就是一个典型的递归问题。

2.2 终止递归的条件(递归出口)

写递归代码的关键是找到如何将一个问题拆分成多个小问题的规律,并且基于此递推公式,再找到递归终止条件,最后将递推公式饿汉终止条件实现成代码即可。

3. 递归的问题

3.1 堆栈溢出

堆栈溢出,造成应用崩溃。

栈指就是栈中的单个元素,一个栈中有很多个栈帧

我们知道操作系统给每一个线程分配了一块独立的栈空间,这种结构称为函数的调用栈。用来存储函数调用时的临时变量,每进入一个函数,就会将临时变量作为一个栈帧入栈,但被调用函数执行结束后,将这个函数对应的栈帧出栈。

每一次的递归调用都向函数调用栈中压入临时变量,知道满足递归终止条件在回归的过程中才会依次将临时变量的栈帧出栈,如果递归调用的层次很深,一直压栈,当栈内存不够用的时间,就出现了堆栈溢出问题。

避免:可以在代码中限制递归调用的最大深度,到达深度后直接返回或者抛出异常。这种方法不能完全解决问题,以为当前线程需要递归的一开始是不知道的,需要去实时的计算深度,也会影响性能,只有在速度不是很大的时候可以使用这种方法防止堆栈溢出。

3.2 重复计算

斐波那契数列问题就是一个典型的重复计算问题。斐波那契数列规则:1,1,2,3,5,…递推公式f(n) = f(n-1) + f(n-2),f(1)=f(2)=1。计算第n项的值,如果我们使用递归来实现就是:

public int fib(int n){//递归实现fib
    if(n==1 || n ==2) return 1;
    return fib(n-1) + fib(n-2);
}

这里就涉及到一个重复计算问题,加入要求解f(5),过程如下:

在这里插入图片描述

可以发现很多数据被重复计算了。如何避免重复计算问题?

为了避免重复计算问题,可以通过一个数据结构(比如散列表)存储已经求解过的f(k),当递归调用到k时,先判断是否已经计算过,如果是直接从散列表中取值返回,避免了重复计算。

4. 递归和循环

递归是静中有动,有趣有回;循环是动静如一,有去无回。递归代码简介,缺点就是空间复杂度高、有堆栈溢出风险、存在重复计算问题、过多的函数调用比较耗时等,如果将递归代码转换为循坏代码,那么性能会提升很多。下面来看斐波那契数列数列问题,计算第n项的值,使用递归:

//时间复杂度O(2^n)
public int fib(int n){//递归实现fib
    if(n==1 || n ==2) return 1;
    return fib(n-1) + fib(n-2);
}

改成循环:(注意一点,二者都没有做越界处理)

//时间复杂度O(n) 性能实现质的飞跃
public int fib(int n){
    if(n == 1 || n == 2) return 1;
    int a=1,b=1,res = 1;
    for(int i=3;i<=n;i++){
        res = a+b;//更新res
        a = b;//更新a
        b = res;//更新b
    }
    return res;
}

对于斐波那契数列数列问题使用循环性能有了质的飞跃,同样的对于汉诺塔等问题递归也适用。注意循环都可以改写成递归,但是递归未必能改写成循环。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值