解读递归算法原理和效率


解读递归算法原理和效率

文章插图
 
对于很多人来说,都知道递归,也能看的懂递归,但在实际项目过程中,却不知道如何使用递归,这里给递归做个总结 。
递归的定义在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法 。实际上,递归,顾名思义,其包含了两个意思:递和归,这正是递归思想的精华所在 。
通俗点讲,我们可以把” 递归 “比喻成 “查字典 “,当你查一个词,发现这个词的解释中某个词仍然不懂,于是你开始查这第二个词 。
可惜,第二个词里仍然有不懂的词,于是查第三个词,这样查下去,直到有一个词的解释是你完全能看懂的,那么递归走到了尽头,然后你开始后退,逐个明白之前查过的每一个词,最终,你明白了最开始那个词的意思 。
递归的思想递归就是有去(递去)有回(归来),如下图所示 。“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去 。最后,从这个临界点开始,原路返回到原点,原问题解决 。
解读递归算法原理和效率

文章插图
 
 
递归的三大要素
  • 明确递归终止条件;
  • 给出递归终止时的处理办法;
  • 提取重复的逻辑,缩小问题规模;
明确递归终止条件我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来 。换句话说,该临界点就是一种简单情境,可以防止无限递归 。
给出递归终止时的处理办法我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案 。一般地,在这种情境下,问题的解决方案是直观的、容易的 。
提取重复的逻辑,缩小问题规模我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决 。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题 。
常见递归算法下面总结一下常见的递归问题和实现算法 。
斐波那契数列斐波那契数列的排列是:0,1,1,2,3,5,8,13,21,34,55,89,144……依次类推下去,你会发现,它后一个数等于前面两个数的和 。在这个数列中的数字,就被称为斐波那契数 。
递归思想:一个数等于前两个数的和 。
首先分析数列的递归表达式:
解读递归算法原理和效率

文章插图
 
代码如下:
/** * 斐波那契数列的递归写法 * @param n * @return */long F(int n){ if (n<=1) return n; return F(n-1)+F(n-2);}可以看到,递归写法简单优美,省去考虑很多边界条件的时间 。当然,递归算法会保存很多的临时数据,类似于堆栈的过程,如果栈深太深,就会造成内存用尽,程序崩溃的现象 。
阶乘递归思想:n! = n * (n-1)!
首先分析数列的递归表达式:
解读递归算法原理和效率

文章插图
代码如下:
long factorial(int n){ if (n <=1) return 1; return j(n-1)*n;}倒序输出一个正整数例如给出正整数 n=12345,希望以各位数的逆序形式输出,即输出54321 。
递归思想:首先输出这个数的个位数,然后再输出前面数字的个位数,直到之前没数字 。
首先分析数列的递归表达式:
解读递归算法原理和效率

文章插图
 
【解读递归算法原理和效率】代码如下:
/** * 倒序输出正整数的各位数 * @param n */void printDigit(int n){ System.out.print(n%10); if (n > 10){ printDigit(n/10); }}汉诺塔数学描述就是:
有三根杆子X,Y,Z 。X杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小 。要求按下列规则将所有圆盘移至Y杆:
  1. 每次只能移动一个圆盘;
  2. 大盘不能叠在小盘上面 。


    推荐阅读