数据结构与算法之递归

递归:分为递和归两个动作。
简单通俗的来说,就是当前步骤发现自己处理不了,那么就“递”出去,让别人处理;
每个步骤都检查自己能不能处理,直到有一个环节能处理,处理完则“归”回来。
这是递归比较形象的说法。

规范一点的说法就是:
1、问题能够拆解成n个小的任务,每个任务的处理逻辑是一样的,这就是一个拆解任务并自己调用自己的过程;
2、任务最终不能再分解,这是一个有最终返回条件,防止无限递归。
总结:找递推公式和终止条件。

实战:
n个台阶,一次可以走一个台阶或者两个台阶,一共有多少种走法
递推公式:
f(n) = f(1) + f(2)
终止条件:
n==1 or n==2

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

分析:
if(n==1) return 1 表示最后剩下一个台阶,那么肯定只有一种走法;
if(n==2) return 2 表示最后剩下两个台阶,那么就有两种走法,一种是一次一个台阶,一种是一次两台台阶。

注意:
1、要注意栈溢出;
2、要考虑重复计算。

问题一栈溢出很好理解,当你不停的递归,当前函数会不断被压入栈中,当递归次数过多,超过栈的大小,就会产生这样的错误提示:
Exception in thread “main” java.lang.StackOverflowError

问题二的重复计算的问题,是因为递归过程中会有一些步骤计算重叠,以上面台阶为例

                    f7
            f6                  f5
    f5          f4          f4      f3
f4      f3   f3     f2      ...     ...
...     ...  ...    ...     ...     ...

我们可以看到从f5往下,都存在重复计算,f5的节点有两个,其实只计算一次就可以了,f4的节点有3个,也是只计算一次就可以了,其他都是如此。
重复计算就意味着浪费性能,需要做针对的优化。
所以上面的函数优化后如下:

Map<Integer, Integer> values = new HashMap<>();
int fun(int n) {
    if(n==1) return 1;
    if(n==2) return 2;
    if(values.containsKey(n)) {
        return values.get(n);
    }
    int result = f(n-1)+f(n-2)
    values.put(n, result)
    return result;
}