递归呀,递归

第一次写博客,谈一谈自己对于递归的理解吧,大神请绕过。

作为一名初学者对递归的灵活应用还是挺困难的。

首先什么是递归呢????????????????

是这样的递归是函数调用的一种特殊形式那特殊在哪呢??这得先看下一般的函数调用,比如说在main函数里调用函数funcA(),而函数funcA()里面又调用了函数funcB().递归是这样子在main函数里调用函数funcA()然后在函数funcA中又调用了函数funcA。听起来有点奇怪自己调用自己好像逻辑上是错误的给人的感觉会形成环,要调用函数funcA必须得先调用函数funcA听起来起来好像是在绕圈子出不来的感觉,所以说递归函数要有出口我们就暂且把funcA称为递归函数吧,函数出口又是什么东东呢?它往往是个if语句,即在funcA函数体有条if语句当满足某种条件时,oh太好了函数可以不需要调用自己直接返回了。说了这么多让人感觉有点晕了,没事下面的图可以加深你对递归的理解。

如图对于普通函数调用过程如下:


Main函数调用funcA函数,然后funcA函数调用funcB函数,在内存中这是一个不断压栈的过程。

funcB中没有在调用其他函数,执行funcB出栈执行funcA,出栈执行main函数,完成任务。


对于递归调用与上述函数调用区别不是太大,如下图所示:

调用过程仍然是一个不断压栈的过程只是可能调用的次数比较多。



然后同普通函数调用一层层退栈。



到这里相信你对递归应该有一点了解了吧,下面举一个使用递归的例子,现在我们要完成这样一件事:有一个容器,容器里面有n个重量不等的小球,要求我们对他们按照重量由大到小进行排序。

很明显这题不用递归也能做,假设我们用递归来做的话,首先构造递归函数sortByWeight(list,array)参数array为装有小球的容器,参数list为新容器,小球会按照顺序在新容器中摆放,介绍完个参数的含义之后介绍一下这个函数所完成的功能:每次在array中取最大的放在list中,每次在array中抽取最大的就能完成排序吗?是的可以。这里的每次让人听起来好像有循环的感觉,递归这里确实有点类似for循环,结束条件是什么呢?还记得前面提到的递归要有出口吗?你猜对了,递归函数的结束条件就是前面提到的出口。本来递归函数是这样不断的调用自己又调用自己又调用自己。。。。。,每次调用自己所传的参数是不一样的?什么意思呢?就是每次被调用函数的listarray参数和主调函数中的listarray是不一样的,这里把主调函数称为父亲被调函数称为孩子吧。每次父亲完成一点工作量并且告诉孩子:老爸年纪大了,接下的事情你去做吧,按我的套路来就行。做完把结果告诉我,我再把结果提交给你爷爷,然后有了爷爷有了自己儿子返回的结果,爷爷整理下再交给自己的父亲,然后一层层的上交交到最原始的祖先手里时,oh天哪这个任务总算完成了。扯的有点远了这里再接着说下咱们的sortByWeight函数。先上伪代码

void sortByWeight(List<小球> list,Array<小球> array){

if(array.size()==0){

return ;

}

else{

小球 maxBall = findMax(array);

list.add(maxBall);

array.remove(maxBall);

sortByWeight(list,array):

}

}

相信大家完全可以看懂这个函数,不过这里我还是需要解释一下。假设我们在main函数中调用sortByWeight函数吧,我们把最原始的sortByWeight函数称为祖先,什么是最原始的呢,哈哈这是我自己给起的名字就是在main函数中直接调用的那个递归函数。其它调用都是在自身中调用自身了。祖先接过参数之后首先看array容器中有没有小球,要是没有正好那还排个毛线序直接返回就可以。如果有小球的话祖先手里有个神器findMax,这个神器能完成这样一种功能可以在容器中的一堆小球里找到最大的。它时

怎么做到的呢。祖先说我管它怎么做到的呢我只看结果反正就是能给我找到(这就像当时鬼子的工兵能用某种工具扫雷,工兵可能也不知道工具的内在 原理也不需要知道 会使用就足够了,涉及到函数封装了扯远了),祖先找到最重的小球之后会把小球从原始容器中拿出来放到list中的相应位置(对的是相应位置,这里不是瞎放的不然又无序了),然后array容器中还剩了好多小球呀,并且这些小球还都是无序的。然后祖先想得给年轻人锻炼的机会呀,于是祖先把自己孩子叫过来把listarray放到孩子的手里,深深的握着他的手告诉他:孩子你长大了需要锻炼了,步骤我都给你了,你就按照这个步骤把这俩个容器的数据处理下吧,处理完告诉我一声哈你爷爷还等着我返回结果呢。然后孩子一步一步传给自己的子孙,最后在某个大雪纷飞的冬天传到某代的时候,这孩子按照祖上的步骤一步步对数据进行处理。突然欣喜的发现oh  my god,array容器中已经没有小球了,这个任务到我这代终于终结了然后他去告诉自己的父亲,然后父亲交给自己的父亲(好吧,这里暂且假设他们都是长生不老的),然后一层层上交最后交到原始祖先的手里(即直接在main函数中调用的那个),这个过程就是回溯。然后原始祖先骄傲的对main函数说:老大你交给我的任务我完成了。

下面让我们猜一下他们的祖传之宝findMax是怎么在一堆球里把最大的一个挑出来的。好像不管怎样每个小球都得称一次,这样祖先把n个小球都称了孩子称的是n-1,孩子的孩子称n-2个小球。。。。。发现了什么,好像做了大量大量的重复工作呀。别着急下一章的动态规划能够解决重复工作的问题。

总结一下:

什么是递归:递归是一种函数调用,只不过这个函数调用有点特殊是自己调用自己而已。

什么时候会用到递归:当某个工程很复杂(我们这里举的小球排序问题只是为了解释递归机制而已)直接做有点让人抓狂这时候我们可以看看问题是否能够分解,如果能先解决问题的一小部分并且剩下的部分还能用自身这个方法来解时才是可用递归。(比如迷宫问题,问从入口A到出口B是否存在路径,当这个迷宫超级大时直接做感觉晕晕的那叫一个酸爽,此时可以看从A能到自己相邻的哪几个位置然后再看从相邻的位置到出口是否存在路径)

如何使用递归:作为一名初学者,我的感觉是写递归的时候要从胸怀全局不去过分考虑细节,什么意思呢,就是把自己当原是祖先,就考虑自己要做什么孩子要做什么,当这个任务到达什么状态时就算这个任务完成了,不去考虑孩子完成后怎么一步步回溯,不去考虑数据传到孙子那会是什么样孙子的孩子那又会是什么样(过度考虑细节就把自己整蒙圈了)。递归函数写好之后再根据这个函数去想细节想怎么传下去,参数是值传递还是地址传递(在函数调用中要特别注意这个,递归是一种特殊的函数调用当然更得仔细考虑了)子孙把任务完成后怎么一层层的提交从而来判断程序的正确性。

递归有什么缺点:正如你所看到的,递归容易造成一些数据的重复计算,双亲已经计算的东西因为并没有保存下来孩子这一代可能还得计算,很浪费时间。还有递归一旦出错debug起来也是有点麻烦,如果是普通函数调用设置断点debug会在不同的函数中跳来跳去比较容易检查错误,递归一直是在执行同一个函数体并且可能刚刚是祖先在干活然后

把任务交给父亲了然后。。在某个阶段又回溯回来了又是祖先在执行任务。所以debug递归函数务必要知道每次都是谁在干活。但是很遗憾这对于初学者来说有时并不是太容易。

就啰嗦到这吧,以上就是我个人对递归的一丁点理解,但愿能帮到像我一样的初学者。另外第一次写博客不当之处还请大家多多包涵。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值