递归函数设计技巧

目录

1.路飞吃桃子的问题--应试难度

2.弹簧板问题--应试难度

3.递归实现指数型枚举--校招难度

4.递归实现组合型枚举--校招难度

5.递归实现排列型枚举--校招难度


1.路飞吃桃子的问题--应试难度

我们可以说下两个案例,反正是最后一天的时候,只剩下了一个桃子,因此如果我们输入的这个天数是2,这个时候就是4个,这个是怎么计算的呢?

就是我们原来的这个桃子的数量是x,这个时候一天之后就是x除以2-1,这个就是剩下的桃子的数量,因此我们想要倒退回去,根据这个数学表达式,应该就是加上1然后乘上2,我们的这个最后的数量是1,加上1乘以2就是4个;

同理,我们在看一下3天的桃子数量,上面是4,(4+1)*2=10个,这个就是第三天的数量; 

根据上面的这个思路,我们进行这个代码的编写,首先这个fn就是我们的保证吃n天的这个桃子的数量,当n=2的时候,这个时候我们应该输出的就是4,因为我们的输入为2天,这个第二天就是最后一天,这个时候就剩下了一个;

我们的f(n-1)就是能够n-1天吃的这个桃子的数量,这个时候根据上面的的那个数学关系,fn=(f(n-1)+1)*2这个数量,最后就可以去设计递归函数了;

2.弹簧板问题--应试难度

下面的这个就是弹簧板问题的题目描述:

下面的这个就是上面题目的案例,我们可以通过案例帮助我们更好地理解这个弹簧板的设计的过程,就是这个小球具体是怎么搞的,怎么弹出去的,下面的第一个样例输入里面,我们的这个5表示的是有5个节点的数据,第一个2表示从这个位置开始的时候,我们会弹出2个位置,这个时候就到了第三个数字3这个位置,3表示的就是到达这个地方之后,我们向后弹出去3个位置,这个时候就会直接弹出去了,因此这个过程就是只需要弹2次,就可以弹出这个弹簧板了;

下面的这个就是递归函数的设计思路:
这个i表示的就是这个数组的下标,我们的上面输入5的时候不是有5个数据吗,这个就是一个数组,我们把输入的这个数组当做一个下标;

i>n(第一次输入的数字)的时候,这个时候可不就已经跳出去这个弹簧板了吗,这个就是我们的递归的临界条件;

fi表示的就是第i个下标的这个板子,fi+a[i]表示的从这个板子开始弹出a[i]之后的这个位置,为什么是加上1呢,因为这个只需要跳一次啊;

按照上面的这个设计思路,我们进行下面的这个代码的编写,这个代码是使用的C++进行编写的,这个里面用到了vector对于我们输入的数据进行存储,我们每输入一个数据,这个数据就会被push_back到我们的这个数组里面去,同时这个n作为循环的控制条件判断我们的这个输入是不是结束了;

在f这个函数里面,我们使用引用进行接收,这个可以减少一次数组的拷贝,然后就是这个递归函数的实现,n表示的就是我们的数组元素的个数,vector<int>表示的就是我们的这个容器里面都是int类型的数据;

3.递归实现指数型枚举--校招难度

我们的这个递归函数里面是有三个参数,第一个就是i表示的就是在第i个位置填写数据,j表示的就是这个位置填写的最小的数据,n表示的就是这个位置填写的最大的数据;

第三步就是怎么让这个程序走通,我们使用的方法就是f(i,j,n)=j+f(i+1,j+1,n)就是我们的第i个位置填上这个数字j之后,我们的下一位置只能填写数字j+1了;

print_one_result就是对于这个数组输出的结果进行打印;

4.递归实现组合型枚举--校招难度

这个题目其实和上面的没有很大的区别,主要就是这个题目限制我们输出的数字的数量,我们输入的两个参数里面有一个参数控制我们输出的数量,但是大部分都是一样的;

这个里面的递归函数,我们设置了4个参数,前面的三个参数的含义不变,其中这个里面的第四个参数表示的就是我们的输出的个数,就是每一次输出是几个数字,在上面的这个案例里面,样例1输出的是每一次都有2个,样例2输出的是每一次都有3个;

我们的代码里面的四个参数如main函数所示,其中这个里面还是有一个print_one函数负责对于这个数据的打印;

f递归函数,首先判断我们的这个终止条件:当我们的这个i==m就是我们的这个数字的个数等于要输出的这个数字的个数的时候,我们就会直接进行return语句,因为这个第一次的时候这个i=0,然后进入这个for循环里面进行第一次递归的时候,这个i就是1了,以此类推我们的这个i==m说明我们的这个输出的数据已经够了,这个时候就会停止输出;

但是我们的第i个位置的数据不可以太大,因为如果我们的这个数据最大就是5,然后我们的这个第i个数字就是5的话,这个时候后面就没有数据可以放置(因为这个数据是按照递增顺序进行排序的),所以我们的这个n-k(数据的个数)>=m-i-1(可以放置数据的空间容量的大小);当然这个就是提高代码的效率,不放也是可以的,只写一个k<=n也是可以正常输出结果的,加上上面的这个就是对于我们的这个输出进行优化罢了;

5.递归实现排列型枚举--校招难度

下面的这个就是实现的我们的排列型枚举,这个枚举和上面的区别就是我们的这个枚举的过程,可以是不按照一个递增的顺序进行排列,就是我们的这个后面的数字的大小可以小于前面的数字;

这个时候,我们的思路就稍稍有了一些变化,我们定义一个新的viso数组,这个数组里面的数据全部都是0,1,其中这个里面的1表示这个位置的数据已经被使用过了,我们这个时候就不可以使用这个数据了,如果这个位置的数据是0,表示这个数据没有被使用,我们可以使用这个数据进行填写

下面的这个代码就是按照上面的这个思路进行编写的,print用来进行每一组数据的打印输出控制,和之前的两道题目都是一样的;

f函数里面的首先就是一个if分支语句,这个语句就是判断我们的这个数组里面的元素是不是凑成了一组数据,如果是等于n说明这个就是一组数据,我们直接调用这个print函数进行打印即可;

如果不是的话,我们就要使用下面的这个for循环进行判断,这个for循环里面就会用到我们的上面的这个viso这个数组,因为我们刚开始的时候定义这个数组全部初始化为0,表示这个数组里面的元素没有被使用过,viso[k]不为0,这个时候,说明这个数字就被使用过,我们这个时候直接continue跳过这个数字;

否则,这个数组这个位置的元素就是0,没有被使用过,我们就要向这个数组里面填数据,arr[i]=k表示把这个没有用过的数据填充到k下标的位置,用过之后这个viso[k]就要置为1,表示这个已经被用过了,然后就是填充这个i+1位置,进行递归,下面的这个viso[k]=0表示这个数字又没有使用过了;

这个是什么意思呢,因为我们上面的这个f(i+1,n)不断地进行递归之后,我们的这个已经全部使用了一遍,例如,我们上面的这个是123   132这种,这个时候,我们的1开头的已经全部列举完成了,因此我们需要已2开头进行列举,这个时候,我们之前的这个所有的标记就是需要清零,重新以2作为起始数据开始列举,因此这个viso[k]=0相当于就是清空之前的操作,开始新一轮的列举(这个很重要!!!);

为什么这个循环里面的k从1开始,而且终止条件是k<=n呢,这个开始我也不是很理解,后来发现,其实这个k表示的根本不是下标,而是我们的数据,例如我们输入的这个数据是5,这个k循环的时候就是1,2,3,4,5这个k就是从1开始的,表示的最大的数据就是我们的输入的n的数值;

反正这个for循环里面的内容不是很好理解。思路很清理,具体如何去进行实现还是需要我们多去揣摩的~~

上一篇:Visual Studio-X64汇编编写


下一篇:实时美颜功能技术揭秘:视频美颜SDK与API的技术剖析