1:递归的传统理解
如果一个问题能够递归,那么:
当前问题能够分解为少量结构相同或相似的子问题组合,子问题同样可以按当前问题的分解方法分解直至到问题边界
解出边界解
从边界往回一层一层得子问题的解,最终得到主问题解
2:浅显易懂的解释
现在来解决一个大问题,问题规模为n。
太难了,一下子解决不动,没关系,试试能不能解决更小规模如n-1的子问题,
还是太难了,一下子解决不动,没关系,试试能不能解决更小规模如n-2的子问题,
......
还是太难了,一下子解决不动,没关系,试试能不能解决更小规模如n=1的子问题,
能啊,只要return 10;(举例子)
在这个过程中,我们不一下解决规模为n的巨大问题,而是不断简化问题规模并发问能否解决此时更简化的问题(即上文中从规模n开始,不断缩减问题规模为n-1,n-2,......,并尝试求解的过程)。
这个不断简化并尝试求解的过程持续进行,直至我们能轻易解决某个很简化的问题(如上文从规模n不断化简直至n=1时,我们能轻松的得到解,这个解可能来源于题意)。
此时这个把规模为n的巨大问题化简的过程就完成了。化简到达了边界,上文的n=1就是边界条件,return 10;的语句就是边界的解。
以上过程为从外到里,逐层化简问题,不断尝试,最终得到边界解。
而这个边界解会给我们带来巨大的回馈。
此时得到了规模为n=1的子问题的解,也就是我们的边界解,
惊喜地发现,问题规模为n=2的子问题可以解决了,只要return 2*(n=1时问题的结果);(举例子,假设随着问题规模的扩大,某问题规模如i的问题的解等于问题规模为i-1的问题的解的2倍)
继续向外,惊喜地发现,问题规模为n=3的子问题可以解决了,只要return 2*(n=2时问题的结果);
......
继续向外,惊喜地发现,问题规模为n的子问题可以解决了,只要return 2*(n=n-1时问题的结果);
以上整个过程,是从最大规模出发,由外向内化简原问题为一个个子问题,最终得到边界解,再由边界出发由内向外逐层得到各个子问题的解,最终得到最大规模问题的解。
3:配合例子食用
举个简单例子,求阶乘。要求解100的阶乘
从最大规模n=100出发,由外向里化简原问题为一个个子问题
n=100,我一下解不出来,没关系,试试n=99子问题能不能解,
n=99,我一下解不出来,没关系,试试n=98子问题能不能解,
......
最终得到边界解
试试n=1子问题能不能解,能解,return 1;
再由边界出发由内向外逐层得到各个子问题的解,
发现,n=2子问题能解了,只要return 2*(n=1时问题的解);
发现,n=3子问题能解了,只要return 3*(n=2时问题的解);
......
最终得到最大规模问题的解
发现,n=100原问题能解了,只要return 100*(n=99时问题的解),
4:简单理解后,如何写出递归:
4.1先得到边界解
分析原问题的小规模形式是怎样的,不断简化原问题规模并尝试求解,直至到最小规模也就是我们的边界,解出来即可。
比如,打印n行数字,它的小规模形式为打印更少行数字,不断化简并尝试打印n-1行,n-2行......,都不好解决,直至n=1可解,打印一个1(举例)即可。
n=1是我们的边界,打印一个1是这个边界的解。
4.2用子问题的解设计递归体
我们得到边界解了。通过边界解的不断返回,能得到各个规模子问题的解。
也就是说,各个规模子问题的解是已知的,要积极主动拿着用。
现在来设计递归体。
只考虑当前规模n,而n-1规模已经解决,我拿来用。
比如,阶乘(n)的递归体为
{
ruturn 阶乘(n-1);//积极主动地用子问题的解
}
在下边代码中,可以看出,主动使用子问题的解是很重要的。否则,递归不成立。
4.3成功
类型 fun(规模 n){
if(n==边界){
return 边界解;
}
else{//递归体,假设更小规模已解决,只考虑当前规模时,如何解决问题
只考虑当前规模的解题过程;
......;
只考虑当前规模的解题过程;
//积极主动用子问题的解,例如fun(n-1)是子问题的解,拿来做乘法(举例子)
ruturn n*fun(n-1);
//用子问题的解,这个行为本身,就是在递归调用自己。
//不用子问题的解,递归不存在。
}
}