递归和尾递归

递归(recursion)

递归指函数体中直接或间接调用自身的一种方法,递归的解决思路通常是把一个大问题转化为许多结构相似的小问题,递归包括三个重要因素:边界条件、前进段、返回段,当边界条件满足时,递归前进(改变参数调用自身),反之则返回。

具体问题一:阶乘

下面用递归来实现阶乘

    private int factorial(int n){
        if(n < 2)
            return 1;
        else
            return n * factorial(n - 1);
    }

这里,边界条件就是n < 2,当n等于0或者1的时候,结果都是1,那为什么不写小于等于1呢?因为那样写执行效率不如小于高。
经过测试,这个函数确实解决了问题,但是考虑函数执行的原理,在执行最后一句时,原函数在执行factorial(n - 1)之前需要把自身的参数压栈,然后等n - 1调用结束后才能出栈释放资源,那么如果这种调用次数很多,就会stackoverflow,有没有办法解决这个问题呢?答案是肯定的,一种是使用迭代,很多时候用递归能够解决的问题,用迭代也能解决,但是代码不如递归好编写好懂,另一种就是在某些语言编译器支持优化尾递归的前提下,可以使用尾递归编写。
把刚才的例子改为尾递归:

    private int factorial(int n, int accumulator){
        if(n < 2)
            return accumulator;
        else
            return factorial(n - 1, n * accumulator);
    }

经过测试,两种方法执行的结果完全一致,但后者在执行最后一句之前,自身的参数已经没有任何用处,此时如果编译器支持优化,完全可以释放自身的资源。
尾递归的特点是将普通递归的结果转为其中的一个参数传递下去,这个参数我通常称它为累加器,如何选取这个累加器的初始值则要根据具体问题分析,比如这个阶乘,始终是相乘的操作,那么初值就应该选1,如果是加减的操作,初值则应该选0。

具体问题二:斐波那契数列(fibonacci array)

嗯,这个巨萌无比的兔子生兔子问题(分裂生殖),我这种极度喜欢兔子的人自然不会放过。上代码,普通递归:

    /**
     * 这里有必要写个注释
     * @param n 数列的第几项(从1开始)
     * */
    private int fibonacci(int n){
        if(n < 1)
            throw new IllegalArgumentException("index of an array must be a positive number");
        if(n < 3)
            return 1;
        else
            return fibonacci(n - 1) + fibonacci(n - 2);
    }

接下来分析尾递归应该怎么写,这个跟阶乘有些区别,前面说过递归其实就是把一个大问题分解成若干个相似的小问题的解决方案,对于阶乘而言,小问题中只依赖n和n - 1,返回值其实是二者相乘,所以改为尾递归时增加了一个累加器参数,而斐波那契数列则是从第三项开始,每一项都是前两项的和,所以需要增加两个参数,下面是具体实现:

    /**
     * 尾递归计算斐波那契数列第n项
     * @param n 要计算第几项(从1开始)
     * @param step 加数
     * @param accumulator 累加器
     * */
    private int fibonacci(int n, int step, int accumulator){
        if(n < 1)
            throw new IllegalArgumentException("index of an array must be a positive number");
        if(n < 3)
            return accumulator;
        else
            return fibonacci(n - 1, accumulator, accumulator + step);
    }

讲真,这个尾递归以我的智商而言非常难写。。。
斐波那契数列其实是从第三项开始计算,所以要求第n项的值,其实加法计算次数是n - 2,当n等于2的时候就不用算了,这个尾递归写法,参数n其实就是个计数器,并不参与加法运算,实际加法运算是由加数和累加器进行的。

总结

理论上来讲,尾递归更加优雅,在支持尾递归优化的语言中,经过优化的尾递归性能远远超过普通递归,java目前似乎还没有支持尾递归优化,但这是趋势,提前使用这种方法,一旦版本更新,就可能收益。但是,尾递归很多情况下不好理解,也更难编写,这种情况就需要测试驱动,先证实它具有跟普通递归相同的作用,再实际使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 函数参数的默认值 基本用法 ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 上面代码检查函数lo...
    oWSQo阅读 238评论 0 0
  • 第八章 递归(recursion) 8.1 导语 因为一些指导者倾向于先教递归作为第一个主要的控制结构,本章会以另...
    geoeee阅读 1,309评论 0 5
  • 好久没写日记了,种种理由,一个字来解释懒啊!今天下载了《简书》,弄了很长时间,也不会。怎么那么笨!请教了宇航妈妈。...
    翔儿妈妈阅读 87评论 0 1
  • 人们总是不愿意讨论自己不够好而又无法改变的或改变成本很大而没有动力去改变的事情。但是有时候这些事情又是我们在工作生...
    无关明暗阅读 211评论 0 0
  • 文|六六 今天晚上想看个电影,就在视频里搜索,下滑了好多页,都没有找到想看的。直到翻到了最后一页,看到了忠爱无言。...
    唯爱香菜阅读 376评论 0 2