浅谈递归

定义

英文定义:Recursion is the process of repeating items in a self-similar way.

具体到计算机中去:

递归:又称为递回,在数学和计算机科学中,是指在函数的定义中使用函数自身的方法.

[以上定义来源为wiki].

英文的Recursion表达的是重复发生,再次重现的意思.而对应的中文翻译”递归”确表达了两个意思:” 递”+”归”.这两个意思,正是递归思想的精髓.

递归思想

递归的基本思想是:把规模大的问题转化为规模小的相似的子问题来解决.这种思想与数学中的归纳法十分相近。

递归在程序中的表现:在函数实现递归时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况.需要注意的是,递归函数必须有明显的结束条件(base case),避免产生无限递归的情况.

经典示例:N的阶乘问题

我们知道5!=5*4*3*2*1;4!=4*3*2*1;…

5!=5*4!...

从上面我们可以归纳出N!=N*(N-1)!,由此函数的代码如下:

public static long factorial(int n){

      if(n==1) // base case

         return 1;

      else  // reduction step

         return n*factorial(n-1);

   }

Base case:基线条件,递归的终止条件

Reduction step:递归操作,对问题的定义.

递归vs循环

从大多数情况来看,递归和循环是可以相互转换的.例如上面的阶乘问题,如果计算的阶乘的方法是n!=n*(n-1)*….*2*1,就明显是一个循环的过程,如果计算阶乘的方法是n=n*(n-1)!,这就归纳出了一个递归定义。循环要求思考一个问题怎么做,而递归要求思考一个问题的定义(某Lisp开发者说的).

实际开发中,循环更加常见,但有些情况下,可能需要将循环转为递归(面试).递归与循环其实想通的地方挺多,它们都需要初始条件和终止条件.用递归解决循环问题,需要明确基线条件和定义递归问题(每一步递归操作的算法),本人在面试过程中遇到过一个这样问题:求两个数的最大公约数,不使用循环.其实,从后面那句”不使用循环”就明显是考递归的。Java代码如下:

// 辗转相除法求两个数的最大公约数

   public static int gcd(int num1,int num2){

      // check args

      if(num1==0 || num2==0){

         return 0;

      }
      // num1>num2

      int tmp = num1;

      num1 = Math.max(num1,num2);

      num2 = Math.min(tmp, num2);

      int p = num1%num2;

      /*while(p!=0){

         num1 = num2;

         num2 = p ;

         p = num1%num2;

      }*/

      if(p!=0)

         return gcd(num2,p);

      return num2;
   }

从上面可以看到,循环转递归时要把握两个点:

  • 循环的终止条件与递归中的基线条件
  • 循环中的算法与递归的算法

应用

当问题的定义本身就是递归形式的时候,可以考虑使用递归.数据结构里面的“树”就是典型的递归定义。还有如上面说的阶乘,汉诺塔问题,斐波拉契数列等.都可以使用来递归解决问题.

使用递归可以使用紧凑和优雅的代码来解决看起来很”吓人”的问题.但实际中,递归并不十分常见.首先,函数执行过程中,栈空间的分配是有限的,如果递归的深度过大,就会造成栈空间的溢出.其次,递归提高了程序的书写效率,但并不意味着执行效率的提高.一般来说,递归的执行效率未必比循环高,因为在栈上开辟新空间、分配局部变量都是需要时间开销的,特别是递归深度很大的时候。所以,学习递归主要是学习其思想—把规模大的问题转化为规模小的相似子问题来解决。

借鉴知乎上某资深码农的一句话,个人觉得说的很好:

当你的问题分析透彻,何时使用递归就会自然明了。递归只是一种函数调用的技巧,不是解决问题的公式。一个问题可以用递归做,也可以不用递归做,递归不是必须的。所以,理解你需要解决的问题,想清楚解决问题的步骤方法,你会突然发现,原来可以用递归来简化问题。不要一开始就想着如何用递归。

参考资料:

http://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92

http://www.zhihu.com/question/20507130

http://www.zhihu.com/question/20096035

http://www.ibm.com/developerworks/cn/linux/l-recurs.html

浅谈递归,布布扣,bubuko.com

时间: 2024-10-13 13:29:27

浅谈递归的相关文章

浅谈递归和分治

今天要谈论的话题主要是递归和分治.为什么要将分治和递归放在一起说?很简单,因为这两兄弟几乎不分家.就我所见过的,任何用到分治思想的算法就没有不用递归的.(如果某位朋友知道例外的,请不吝赐教.) 所谓递归,就是自己调用自己.第一步是设置基值条件用于方法的返回,否则方法将不停的自调用,直到程序崩溃.第二部是缩小问题域的范围并调用自己来求解,直到范围缩小到触发基值条件为止. 所谓分治,即分而治之.它也分两步:一,把一个大的问题域分解为小的问题域.二,对每个小的问题域按步骤一办理.(单从这句话来说,我们

浅谈递归调用的个人领悟

从大一开始学c,就不是挺理解递归的,最近突然有所体会: 递归调用中递归调用的函数可以把它想象成为一个树的结点,在函数中调用自身就是一个分支,直到出口条件时就是这棵树的叶子结点.叶子的值便是出口返回的值.最后从叶子结点按照你所调用的方法向上返回值,最终结束递归调用.

浅谈递归和尾递归(tail recursive)

一.递归 简单来说,递归的思想就是:把问题分解为规模更小的.具有与原问题有着相同解法的问题.比如二分查找算法,就是不断地把问题的规模变小(变成原问题的一半),而新问题与原问题有着相同的解法. 一般来讲,能用递归来解决的问题必须满足两个条件: 可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式. 存在一种基准情形(即出口),可以使递归在基准情形下退出. 以计算n!为例,递归的算法为: public static long factorial(long n){ ????????if(n=

JS 从斐波那契数列浅谈递归

一.前言 昨晚下班后,经理出于兴趣给我们技术组讲了讲算法相关的东西,全程一脸懵逼的听,中途还给我们出了一道比较有趣的爬楼问题,问题如下: 假设一个人从地面开始爬楼梯,规定一步只能爬一坎或者两坎,人只能往上走,例如爬到第一坎,很明显从地面到第一坎只有一种可选方式,从地面爬到第二坎,他可以从地面直接跨到第二坎,也可以先从地面到第一坎,再从第一坎到第二坎,也就是2种可选方式,那么求他爬到N楼一共有几种可选方式. 这道题涉及到了斐波那契数列,要求使用递归来求值,技术贼菜的我也是一脸懵逼,所以本着学习的心

浅谈python中的递归

python 浅谈 递归函数 最近在自学一些python,找了些资料.自己慢慢研究到了递归函数这一章,碰到个很经典的例子.汉诺塔的移动.一开始尝试自己写的时候发现,这东西怎么可能写的出来.但是看到别人写出来以后发现,这东西真的能写出来. 本着借鉴的目的想去分析一下别人写的东西.觉得很有意思想给大家分享一下,如果有误请大家指正首先大家可以先自己想想如何能写出来. 先说一下:所谓的递归,我认为就是不断重复调用.直到return 出当前的递归循环.在我拆分的过程中,大家不妨先自己想一下结果,然后看一下

浅谈算法和数据结构

: 一 栈和队列 http://www.cnblogs.com/yangecnu/p/Introduction-Stack-and-Queue.html 最近晚上在家里看Algorithems,4th Edition,我买的英文版,觉得这本书写的比较浅显易懂,而且“图码并茂”,趁着这次机会打算好好学习做做笔记,这样也会印象深刻,这也是写这一系列文章的原因.另外普林斯顿大学在Coursera 上也有这本书同步的公开课,还有另外一门算法分析课,这门课程的作者也是这本书的作者,两门课都挺不错的. 计算

java serialize 浅谈

对象的串行化(Serialization) 一.串行化的概念和目的 1.什么是串行化             对象的寿命通常随着生成该对象的程序的终止而终止.有时候,可能需要将对象的状态保存下来,在需要时再将对象恢复.我们把对象的这种能记录自己的状态以便将来再生的能力.叫作对象的持续性(persistence).对象通过写出描述自己状态的数值来记录自己 ,这个过程叫对象的串行化(Serialization) .串行化的主要任务是写出对象实例变量的数值.如果交量是另一对象的引用,则引用的对象也要串

递归算法浅谈

递归算法 程序调用自身的编程技巧称为递归( recursion). 一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题类似的规模较小的问题来求解,递归策略仅仅需少量的程序就可描写叙述出解题过程所须要的多次反复计算,大大地降低了程序的代码量. 注意: (1) 递归就是在过程或函数里调用自身; (2) 在使用递增归策略时,必须有一个明白的递归结束条件,称为递归出口. 一个比較经典的描写叙述是老和尚讲故事,他说从前有座山,山上有座庙,庙里有个

浅谈自然语言处理基础(下)

命名实体识别 命名实体的提出源自信息抽取问题,即从报章等非结构化文本中抽取关于公司活动和国防相关活动的结构化信息,而人名.地名.组织机构名.时间和数字表达式结构化信息的关键内容,所以需要从文本中去识别这些实体指称及其类别,即命名实体识别和分类. 21世纪以后,基于大规模语料库的统计方法成为自然语言处理的主流,以下是基于统计模型的命名实体识别方法归纳: 基于CRF的命名实体识别方法 基于CRF的命名实体识别方法简便易行,而且可以获得较好的性能,广泛地应用于人名.地名和组织机构等各种类型命名实体的识