数据机构与算法学习(八)- 递归

递归需要满足三个条件

1. 一个问题的解可以分解为几个子问题的解

2. 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一致。

3. 存在递归终止条件

如何编写递归代码

最关键的是写出递推公式,找到终止条件,剩下将递推公式转化为代码

假如有n个台阶,每次可以跨1个台阶或者2个台阶,请问走这n个台阶有哦多少种走法?

n个台阶的走法就等于先走1台阶后,n-1个台阶的走法加上先走2台阶后,n-2个台阶的走法,用公式表示:

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

有了递推公式,然后在看终止条件。有1台阶有1种走法,有2个台阶的话,有2中走法。所以

f(1) = 1
f(2) = 2

有了终止条件和递推公式,那么最后转换为代码

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

总结一下,写递归代码的关键就是找到如何将大问题分解为小问题的规律,并且基于此写出递推公式,然后在推敲出终止条件,最后将递推公式和终止条件翻译成代码。

递归代码的常见问题

1. 递归代码要警惕堆栈溢出, 函数调用会使用栈来保存临时变量,如果递归很深的话, 一直压栈会有栈溢出风险,一般程序中栈是有大小设置的。

2. 递归代码要警惕重复计算,像刚才n个台阶的那个例子,假设n=5的话,在n=5时会计算f(4)和f(3),在n=4时会计算f(3)和f(2).这样就有了重复计算。

递归代码改为非递归代码

还用n个台阶的例子

int f(int n){
    if (n ==1 ) return 1;
    if( n == 2) return 2;

    int ret = 0;
    int pre = 2;
    int prepre = 1;
    for(int i = 3; i <= n; ++i){
        ret = pre + prepre;
        prepre = pre;
        pre = ret;
    }
    return ret;
}

笼统的讲,所有的递归代码都可以改为非递归的迭代循环方式。因为递归本身就是借助栈来实现的,只不过我们使用的栈是系统或者虚拟机本身提供的,我们没有感知罢了。

 

posted on 2021-04-06 08:02  成长的皮球  阅读(85)  评论(0编辑  收藏  举报