最近做类的静态数据成员的题目碰到一道母牛生小牛,虽然这道题跟递归没什么关系,但是题目说不能用f(n)=f(n-1)+f(n-3),我想了好久都想不明白怎么就f(n)=f(n-1)+f(n-3)了,之前虽然偶尔碰到过递归但是一直都没有系统地了解过,仔细想想我甚至都说不出什么是递归,那就借此机会好好理解一下递归,也算是提前预习一下数据结构和算法。
先看这道“母牛生小牛”——设有一头小母牛,从出生第四年起每年生一头小母牛,按此规律,第N年时有几头母牛?:
显然第N年的母牛要么本身就是之前的牛要么是之前的牛生的牛对吧,不会再突然加进来新的牛了,那这个肯定可以递推啊!
第一感觉 ==> 第N年的所有牛f() = 第N年新出生的牛fnew()和第N年不是新出生的牛fold() ==> f(N)=fold(N)+fnew(N)
如果能把fnew(N)和fold(N)都用f(N-?)即之前第?年的所有牛表示 ==> f(N)=f(N-?)+f(N-?) ==> 那么递推公式就出来了
第N年不是新出生的牛fold()显然就是去年的所有牛啊 ==> fold(N) = f(N-1)
第N年新出生的牛 <==> 第N年能生牛的牛生的牛 <==> 第N年4岁及4岁以上的牛 <==> 第N年>=4岁的牛
第N年>=4岁的牛能用之前某一年的所有牛f(?)表示吗,毕竟f(N)递推公式里的自变量肯定不能是N自己不然就死循环了
每一年的牛肯定都比之前任何一年的牛多,如果第N年>=4岁的牛可以往下展开成>=1岁的牛,那不就正好是那一年的所有牛了,那就可以用f(N-?)来表示fnew(N)了
第N年>=4岁的牛 <==> 第N-1年3岁和>=4岁的牛 <==> 第N-2年2岁3岁和>=4岁的牛 <==> 第N-3年1岁2岁3岁和>=4岁的牛
这个第N-3年1岁2岁3岁和>=4岁的牛不就是第N-3年的所有牛f(N-3)吗
为什么第N年新出生的牛正好是三年前的所有牛呢?==> 第N年新出生的牛=第N年能生小牛的牛 ==> 牛4岁的时候也就是出生的三年以后才能生小牛 ==> 今年的所有牛三年以后都满4岁了所以全部都能生小牛,明年的牛和后年的牛在第三年的时候都还有没满4岁的牛所以并不是全部都能生小牛 ==> 第N年新出生的牛就是三年前的所有牛 ==> fnew(N) = f(N-3)
∴ f(N) = fold(N) + fnew(N) <==> f(N) = f(N-1) + f(N-3) <==> 即第N年的所有牛=一年前的所有牛+三年前的所有牛
画个图验证一下好像确实没毛病:
接着是“约瑟夫环”——这道题出现过太多次了,从循环到链表到双链表再到异常处理,当时我考虑过递归但是确实写不来:
0,1,,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
由题意有两个变量需要我们给定数字总数n和删除间隔m,所以设:f(n,m)——总共有n个数字[0,n-1]且删除间隔为m的情况下留到最后的数字的下标
如果我们用固定队列进行删除:
第一次变化:f(n,m)——从n个数[0,n-1]中删除掉从头开始的第m个数然后把第m个数后面的数都向前移一位 ==> f(n-1,m);
第二次变化:f(n-1,m)——从n-1个数[0,n-2]中删除掉从头开始的第m个数然后把第m个数后面的数都向前移一位 ==> f(n-2,m);
但是这种情况删除间隔为1而不是m,要保证按间隔m循环删除,除非多添加一个数组参数记录每次删除后的位置并作为下一次删除的起始位置,但这好像也太麻烦了,那如果把f(n,m)改成f(n,m%n),把第f(n-1,m)中二个参数m改成(m%n+m)%n <==> 2m%n,也就是f(n-1,2m%n),这样第二次变化也可以以m的间隔进行删除了,所以f(n,m)应改为——f(n,(数字总个数-n+1)*m%n);
易知此时f(n,m%n)与f(n-1,2m%n)有两种情况显然为:①f(n,m%n) = f(n-1,2m%n) ②f(n,m%n) = f(n-1,2m%n) + 1
显然①≠②,这就有二义性没法实现了,所以固定队列的思路是行不通的。
那如果用循环队列进行删除: