09.递归

1.递归三条件

​ 周末你带着女朋友去电影院看电影,女朋友问你,咱们现在坐在第几排啊?电影院里面太黑了,看不清,没法数,现在你怎么办?

​ 别忘了你是程序员,这个可难不倒你,递归就开始排上用场了。于是你就问前面一排的人他是第几排,你想只要在他的数字上加一,就知道自己在哪一排了。但是,前面的人也看不清啊,所以他也问他前面的人。就这样一排一排往前问,直到问到第一排的人,说我在第一排,然后再这样一排一排再把数字传回来。直到你前面的人告诉你他在哪一排,于是你就知道答案了。

​ 这就是一个非常标准的递归求解问题的分解过程,去的过程叫**“递”,回来的过程叫“归”**。基本上,所有的递归问题都可以用递推公式来表示。刚刚这个生活中的例子,我们用递推公式将它表示出来就是这样的

f(n) = f(n-1) + 1

相应的代码如下:

public int f(n) {
    if(n == 1) {
        return 1;
    }
    
    return f(n-1) + 1;
}

1.1 一个问题可分解为子问题

​ 子问题:就是数据规模更小的问题,拿前面的问题,要想知道你在那一排,可以分解为”前一排的人在那一排“这样的子问题。

1.2 分解完后,问题与子问题除了数据规模不一样,求解思路完全一样

​ 前一排的人求解他自己在哪一排的方法,和我再那一排的方法完全一样。

1.3 存在递归终止条件

​ 需要有终止条件,不然会陷入死循环,比如前面问题,第一排知道自己的位置,所以f(1) = 1就是终止条件。

2.如何写递归代码

2.1 将大问题化为小问题,写出地推公式,找到终止条件

只考虑当前级别和下一级别的问题,不去思考层层迭代问题。比如,现在呢总共有7个台阶,一次可以走一阶,也可以走两阶,请问有几种走法?可以将问题分解为:只有两层:这一层和下一层的问题。如果第一次走1部,则下一层剩下 (n-1)个台阶;如果第一次走两步,则下一层只剩下(n-2)个台阶。因此递推公式为:

f(n) = f(n-1) + f(n-2);

终止条件为:f(1)=1. 这个条件满足条件吗?用n=1,2,3,4试一下:n=1, f(2) = f(1) + f(0), 变成了 f(1)=0,f(0)=1这两个终止条件。f(0)=1,走0个台阶有1中走发。这个有点奇怪。优化一下,变成 f(1)=1, f(2)=2. 当n > 2,都可以拆成 f(1)与f(2)的组合。

2.2 翻译成代码

 /**
     * f(1)=1,f(0)=1
     * @param n
     * @return
     */
    public static long fstrange(int n) {
        if(n == 1) return 1l;
        if(n == 0) return 1l;

        return f(n-1) + f(n-2);
    }

    /**
     * no cache.
     * @param n
     * @return
     */
    public static long f(int n) {
        if(n == 1) return 1l;
        if(n == 2) return 2l;

        return f(n-1) + f(n-2);
    }

3.注意实现

3.1 堆栈溢出

栈-栈帧有自己的空间,用于存储局部变量表和操作数栈等,如果太深,会导致内存不够发送内存溢出问题。

3.2 重复计算

在这里插入图片描述

上图中的 f(4)/f(3)/f(2)/…等都会被重复计算多次,时间复杂度很高,可以采取空间换时间的思路(其实空间复杂度不一定比递归高,因为递归有函数栈帧开销),可以将计算好的结果存起来,后面直接从缓存里面读取。代码如下:

/**
     * cache.
     * @param n
     * @return
     */
    public static long fwithcache(int n) {
        if(n == 1) return 1;
        if(n == 2) return 2;

        if(map.containsKey(n)) {
            return map.get(n);
        }

        long ret = fwithcache(n-1) + fwithcache(n-2);

        map.put(n, ret);

        return ret;
    }

用我的2017年macPro比较了一下,计算n=50次的规模,加缓存比不加缓存多用了 50.186s(50188-2) :

 public static void main(String[] args) {
        System.out.println(f(1));
        System.out.println(f(2));

        System.out.println(f(10) + "==" + fstrange(10) + "?");

        // 无缓存
        long l = System.currentTimeMillis();
        System.out.println(f(50));
        System.out.println(System.currentTimeMillis() - l);

        // 有缓存
        l = System.currentTimeMillis();
        System.out.println(fwithcache(50));
        System.out.println(System.currentTimeMillis() - l);
    }

Output:

1
2
89==89?
20365011074
50188
20365011074
2
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用for循环和归函数求1+2+3+...+n的值的代码: 使用for循环: ```python n = int(input("请输入一个正整数n:")) sum = 0 for i in range(1, n+1): sum += i print("1+2+3+...+n的值为:", sum) ``` 使用归函数: ```python def sum_recursive(n): if n == 1: return 1 else: return n + sum_recursive(n-1) n = int(input("请输入一个正整数n:")) sum = sum_recursive(n) print("1+2+3+...+n的值为:", sum) ``` 以下是使用for循环和归函数求n的阶乘的代码: 使用for循环: ```python n = int(input("请输入一个正整数n:")) result = 1 for i in range(1, n+1): result *= i print("n的阶乘为:", result) ``` 使用归函数: ```python def factorial_recursive(n): if n == 0: return 1 else: return n * factorial_recursive(n-1) n = int(input("请输入一个正整数n:")) result = factorial_recursive(n) print("n的阶乘为:", result) ``` 以下是使用for循环和归函数求斐波那契数列第n项的代码: 使用for循环: ```python n = int(input("请输入一个正整数n:")) if n == 1 or n == 2: result = 1 else: pre1 = 1 pre2 = 1 for i in range(3, n+1): result = pre1 + pre2 pre2 = pre1 pre1 = result print("斐波那契数列第n项的值为:", result) ``` 使用归函数: ```python def fibonacci_recursive(n): if n == 1 or n == 2: return 1 else: return fibonacci_recursive(n-1) + fibonacci_recursive(n-2) n = int(input("请输入一个正整数n:")) result = fibonacci_recursive(n) print("斐波那契数列第n项的值为:", result) ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值