递归算法的理解以及调优(尾递归)

提到递归首先想到的就是效率低下,但是为什么低下,看下下面的一段代码

public static void main(String[] args) {
        Integer result = recursion(5);
        System.out.println(result);
    }

    static Integer recursion(Integer n) {
        if (n < 1) {
            return 1;
        } else {
            return n * recursion((n - 1));
        }
    }
}

这段代码的执行过程:

开始
线性递归每次递归都会在内存中开辟新的空间存储结果等待结果:
5 * recursion((4)) 4 * recursion((3)) 3 * recursion((2)) 2 * recursion((1)) 得到结果逐层返回 2 6 24 120
resul=120
结束

下面给出优化版本的递归,也就是尾递归

  public static void main(String[] args) {
        Integer result = recursion(1, 5);
        System.out.println(result);
    }

    static Integer recursion(Integer a, Integer n) {
        if (n == 0) {
            return a;
        } else {
            return recursion(a * n, n - 1);
        }
    }
}

 尾递归的执行过程:

开始
recursion(1*5,5)
recursion(5*4,4)
recursion(20*3,3)
recursion(60*2,2)
recursion(120*1,1)
result=120 结束

 

分析:

线性递归因为每次递归都要等待下面结果的返回,随着n的增加,时间复杂度和空间复杂度都是线性增长

尾递归中的n和a已经保存了递归的结果和深度,无需依赖下次递归的结果,每次递归系统无需开辟新的空间去记录中间值

 

好,接下来来一个进阶的题

题目:使用递归实现fibonacci(斐波那契数列),就是输入n,则算法fibonacci输出队列的第n项,fibonacci队列的定义是:1,1,2,3,5,8,13,21,34,55,89......,fibonacci的通项公式是f(n) = f(n-1) + f(n-2)

思考一下实现形式......

  • 初级实现方式:
public static void main(String[] args) {
        int result = recursion(10);
        System.out.println(result);
    }

    static Integer recursion(Integer n) {
        if (n < 2) {
            return 1;
        } else {
            return recursion(n - 1) + recursion(n - 2);
        }
    }
}

分析:以上就是使用了两个线性递归进行通项公式的实现

  • 中级的实现方式:

思考:有大于一个的线性递归,使用空间换时间,在线程进行第一次递归的时候是不是能将递归的结果保存下来,当第二次线性递归的时候直接从变量里面去取,以下是代码实现

package com.zzq.controller;

import java.text.ParseException;

/**
 * @author zhangzhaoqiang
 */
public class Application {
    final int MAX_N = 2048;
    private int[] _cacheInt = new int[MAX_N];

    public static void main(String[] args) {
        Application application = new Application();
        int result = application.recursion(10);
        System.out.println(result);
    }

    Integer recursion(Integer n) {
        if ( _cacheInt[n] != 0 ) {
            return _cacheInt[n];
        }
        if (n <= 1) {
            _cacheInt[n] = 1;
            return 1;
        } else {
            _cacheInt[n] = recursion(n-1)+recursion(n-2);
            return _cacheInt[n];
        }
    }
}

 

分析:因为两个递归调用都是在一个线程中完成,所以在第一次递归的时候将递归的中间结果保存下来,这样在进行第二次递归的时候就能直接从_cacheInt取到

  • 高级实现方式

思考:采用尾部递归的实现方式来实现,上代码

public class Application {
    public static void main(String[] args) {
        Application application = new Application();
        int result = application.recursion(10);
        System.out.println(result);
    }

    int recursion(int n) {
        return recursion(1, 0, n);
    }

    int recursion(int a, int b, int n) {
        if (n == 0) {
            return a;
        } else {
            return recursion(a + b, a, n - 1);
        }
    }
}

分析:上面就采用尾递归的实现方式进行实现

 

以上是我能想到的三种实现方式,如果有更好的实现方式欢迎留言交流。

posted @ 2019-12-04 16:44  青竹zzq  阅读(383)  评论(0编辑  收藏  举报