递归
做了一段时间的题,越来越感觉想明白递归这件事真的很重要,而且这件事并不是一两天就可以彻底明白所有题目,所有种递归。
学递归更多是通过不断训练,日积月累形成这种递归的思维,因为迭代、递推是与我们正常运算思维相同的,而递归是完全相反的。
比如算阶乘这件事,n!=1*2*3*……*n一般想到的都是从1*2开始算起,再用结果依次向后乘。这是迭代。
而递归怎么算?递归是想,我要算n的阶乘我直接用算式前边(n-1)!的结果乘n就好了。
而(n-1)!该怎么算?那我把(n-2)!结果乘n-1就好了。
……
以此类推,最后一直道2!=1*2无法再分解了;然后再将结果逐层返回。这是递归。
为什么我们思维更习惯迭代?
按照上面的例子顺理成章的想,主因是我们懒,原因如下:
用迭代时,我的想法是只算1*2=2;然后再取后边一个数,再算2*3=6……这样每次只要存一个数,取一个数再做一次运算。
用递归时,造成你我想到n!=(n-1)!*n就烦的是什么?是因为对你来说,(n-1)!他就是个未知数,所以要求他就得再往上找。但是这个n他就要记住,到了下一层时n-1这个数也要记……直道最后一层,1*2=2了;才能返回去前一层做上一次的的运算,当然前提是你得记住前一层的状态。
因为这个简单例子里边都是有序的,差别并不大,但是做其他的递归时,可能涉及到其他许多变量或者说事儿的状态要存储,而我们习惯于能记得多简,就记得多简。
这样大脑思考和运算的感觉更轻松。而且大多的递归都能通过迭代做到(不讨论优劣)。所以我们本能的讨厌递归这种思维,而偏爱迭代。
在算法上更是如此,所以新手碰到递归时一般都不太愿意去做orz。长久以后会发现根本写不好递归,或者说对递归这件事都是畏惧心理,那就完蛋了。
那怎么去学递归?(以下观点纯属个人感觉,走火入魔,后果自负)
①:我觉得首先得建立对 递归的感觉——它是对的,是严谨正确的。
这一点用类似数学归纳法就可以证明
https://www.cnblogs.com/LiCheng-/p/8206444.html这位兄弟有现成的。
思路也比较简单。用Paul Graham的话来理解
- 当n=0, 1的时候, 结果正确.
- 假设函数对于n是正确的, 函数对n+1结果也正确.
如果这两点是成立的,我们知道这个函数对于所有可能的n都是正确的。
另外,我认为所有递归和迭代都可以借助栈或存储空间来互相改写(不考虑改写后效率),所以可以理解为用迭代可以实现的,用正确的递归肯定也能正确实现。(这点有待考察)
②:多写多练,形成一种思维好像没有更好的方式了,怎么绕不开练习,多写代码多想,这个方法简单粗暴好用!
如何写递归?递归有套路吗?
写递归到是有固定的几点。固定套路真的还没发现orz,真是什么样的递归都有。
不过写的时候注意这几点肯定没错:
①:我这个算法用递归到底对不对->是不是对于所有情况都能用相同方式拆分成子问题?是不是求解方式适用于所有情况?
②:写主函数时先写大体框架,用自顶向下的方法写(这是刘汝佳说的)。
③:要时刻记得把所有情况都想清楚:一般情况,特殊情况1,特殊情况2,……,一定要做到严谨,所有情况下算法都正确。
④:递归边界(基)一定要明确且正确。
关于递归函数的返回值和参数:
先说不带引用的:因为只传址,参数可以看成对当前层的状态的描述,比如根据这些状态判断是否继续递归。
而返回值可以是作为上一层计算的结果,例如阶乘的那个例子。
另外,胡思乱想了一下,我们所处的世界大多都是两面相对的,阴阳,正负,对错,物质和反物质,那么有正向思维就有逆向思维。想想既然这些都是人的主观规定,
那么就可以少一点对逆向思维的“特殊”的感觉(毕竟只是你认为的普通思维的相对面),这样也解释了许多很难很难的算法用递归来写非常简单有效。可能是用递归这个从”反面“走的思维,来解决从“反面”来的问题更合适呢。(上边这些纯属是个人纯瞎想,没事别瞎看)。
以上
还有很多很多要总结的,以后遇到想到再慢慢来吧。