![88fd29a92a008cfd7c03e28344bf70d3.png](https://img-blog.csdnimg.cn/img_convert/88fd29a92a008cfd7c03e28344bf70d3.png)
啥是递归算法呢?用比较哲学的话语来形容就是:从前有座山,山里有个庙,庙里有个老和尚,他对小和尚讲故事,讲的是:从前有座山,山里有个庙。。。。。。。。。。。。。这样子的故事,如果是放在高中作文中,可以写上个几万字了。
刚学编程的时候,是比较难理解递归算法的。递归思想之所以困难,原因在于它非常像是一种循环推理,它也不是一个直观的过程,稍微有点绕的感觉。
在日常开发中,我们使用循环语句远远大于递归,但这不能说明递归就没有用武之地。
递归算法是比较好用,但是理解起来可能不太好理解,所以在递归算法和循环算法对比中,流行一句话:人理解循环,神理解递归。当然这只是一个段子,不过也从侧面反映出递归算法不容易理解的事实。
ps:本文为避免繁杂,没有涉及到递归算法的时间/ 空间复杂度
什么是递归
递归算法简单来说就是:自己调用自身函数的算法。只不过其中要涉及要递归出口和逻辑处理的过程,这就增加了设计递归算法的难度。
递归算法的实质是把问题分解成规模缩小的同类问题的子问题,然后递归调用方法来表示问题的解。递归算法对解决一大类问题很有效,它可以使算法简洁和易于理解。
这样我们就可以利用大道至简的思想,把一个大的复杂的问题层层转换为一个小的和原问题相似的问题来求解的这样一种策略。
递归往往能给我们带来非常简洁非常直观的代码形势,从而使我们的编码大大简化,然而递归的思维确实很我们的常规思维相逆的,我们通常都是从上而下的思维问题, 而递归趋势从下往上的进行思维。
这样我们就能看到我们会用很少的语句解决了非常大的问题,所以递归策略的最主要体现就是小的代码量解决了非常复杂的问题。
递归算法解决问题的特点
1)递归就是方法里调用自身。
2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低。所以一般不提倡用递归算法设计程序。
4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等,所以一般不提倡用递归算法设计程序。
public void recursiveTest(){ recursiveTest(); //自己调用自己的方法 ----> 就叫递归}
上面就是最简单的递归算法,但不是正确的递归算法,一旦运行起来就会抛出栈内存溢出的异常,因为没有退出条件,
所以就会进入死循环中,一直都在重复调用自己,递归调用在底层其实是对线程栈的压栈和出栈操作,每调用一次都会压栈一次,并记录相关的局部变量信息,线程栈的内存是非常有限的,而递归调用如果是无限的,那么很快就会消耗完所有的内存资源,最终导致内存溢出。
这一点与空的while死循环是不一样的,单纯的死循环会大量的消耗cpu资源,但不会占用内存资源,所以不会导致程序异常。从这一点能看到递归算法其实是更加消耗系统的性能和资源的。
因此:
使用递归算法必须满足2个条件:
1)有反复执行的过程(调用自身)
2)有跳出反复执行过程的条件(递归出口)
递归算法简单例子
说了那么多,看个例子更加直观:求阶乘。显然,阶乘是可以用循环结构来实现的,因此递归与循环是可以相互转换的。
直接出代码:
public static int factrial(int n){ if(n<1){ return 1; } return n * factrial(n-1); }
以阶层 f(6) 为例来看下它的「递」和「归」。
求解问题 f(6), 由于 f(6) = n * f(5), 所以 f(6) 需要拆解成 f(5) 子问题进行求解,同理 f(5) = n * f(4) ,也需要进一步拆分,... ,直到 f(1), 这是「递」,f(1) = 1解决了,由于 f(2) = 2 f(1) = 2 也解决了,.... f(n)到最后也解决了,这是「归」,所以递归的本质是能把问题拆分成具有相同解决思路的子问题,。。。直到最后被拆解的子问题再也不能拆分,解决了最小粒度可求解的子问题后,在「归」的过程中自然顺其自然地解决了最开始的问题。
另外一种形式:
对应进栈出栈:
递归算法基本框架
java实现递归的基本框架
public void recur(int level, int param) { // level:递归层数;param参数 // 递归终止条件 if (level > MAX_LEVEL) { // 对结果处理 return; } // 处理当前层的逻辑关系 process(level, param); // 递归下去一层 recur( level: level + 1, newParam); // 保存当前状态 }
递归必须要有三个要素:
①、边界条件
②、递归前进段
③、递归返回段
当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
其他递归算法例子
一、汉诺塔问题:
所有盘子的直径是不同的,并且盘子中央都有一个洞使得它们刚好可以放在塔座上。所有的盘子刚开始都放置在A 塔座上。这个难题的目标是将所有的盘子都从塔座A移动到塔座C上,每次只可以移动一个盘子,并且任何一个盘子都不可以放置在比自己小的盘子之上。
无论有多少个盘子,我们都将其看做只有两个盘子。假设有 N 个盘子在塔座A上,我们将其看为两个盘子,其中(N-1)~1个盘子看成是一个盘子,最下面第N个盘子看成是一个盘子,那么解决办法为:
①、先将A塔座的第(N-1)~1个盘子看成是一个盘子,放到中介塔座B上,然后将第N个盘子放到目标塔座C上。
②、然后A塔座为空,看成是中介塔座,B塔座这时候有N-1个盘子,将第(N-2)~1个盘子看成是一个盘子,放到中介塔座A上,然后将B塔座的
第(N-1)号盘子放到目标塔座C上。
③、这时候A塔座上有(N-2)个盘子,B塔座为空,又将B塔座视为中介塔座,重复①,②步骤,直到所有盘子都放到目标塔座C上结束。
public static void move(int dish,String from,String temp,String to){ if(dish == 1){ System.out.println("将盘子"+dish+"从塔座"+from+"移动到目标塔座"+to); }else{ move(dish-1,from,to,temp);//A为初始塔座,B为目标塔座,C为中介塔座 System.out.println("将盘子"+dish+"从塔座"+from+"移动到目标塔座"+to); move(dish-1,temp,from,to);//B为初始塔座,C为目标塔座,A为中介塔座 }}
二、斐波那契数组:
public static int fab(int index) { if (index == 1 || index == 2) { return 1; } else { return fab(index - 1) + fab(index - 2); }
三、排列组合:
要求:将输入的一个字符串中的所有元素进行排序并输出,例如:你给出的参数是"abc",
则程序会输出
abc
acb
bac
bca
cab
cba
public static void permute(char[] list, int low, int high) { int i; if (low == high) { String cout = ""; for (i = 0; i <= high; i++) { cout += list[i]; } System.out.println(cout); } else { for (i = low; i <= high; i++) { char temp = list[low]; list[low] = list[i]; list[i] = temp; permute(list, low + 1, high); temp = list[low]; list[low] = list[i]; list[i] = temp; } }
输出:
public static void permute(String str) { char[] strArray = str.toCharArray(); permute(strArray, 0, strArray.length - 1); }
当然,还有经典的递归遍历算法:二叉树的前序、中序、后序遍历
![d019b1d7ba6647488e690e5ffff3ed26.png](https://img-blog.csdnimg.cn/img_convert/d019b1d7ba6647488e690e5ffff3ed26.png)
END
![df5596dc3df5bffb61d44eb46f7713df.png](https://img-blog.csdnimg.cn/img_convert/df5596dc3df5bffb61d44eb46f7713df.png)
扫码关注
微信号 : LifeAndCoding
数据结构 | 算法
编程语言 | 面经
电子资源 | 资讯
![e0b6327814a7af8f552ee30f03bae18d.gif](https://img-blog.csdnimg.cn/img_convert/e0b6327814a7af8f552ee30f03bae18d.gif)
![e3f836986ff8f6663d8316958880e148.gif](https://img-blog.csdnimg.cn/img_convert/e3f836986ff8f6663d8316958880e148.gif)