递归需要满足三个条件
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; }
笼统的讲,所有的递归代码都可以改为非递归的迭代循环方式。因为递归本身就是借助栈来实现的,只不过我们使用的栈是系统或者虚拟机本身提供的,我们没有感知罢了。