递归

1.递归是什么?

定义:程序调用自身的编程技巧称为递归。
递归使用的是选择结构,对于解决同样问题的孪生兄弟:迭代,它使用的则是循环结构。

2.递归的核心

先看一个例子,表达式1+2+3....+100=?要怎么写程序来计算?
第一反应是for循环:

int sum =0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
System.out.println(sum);//5050

更简洁的写法

int start =1;
int end = 100;
int sum =(start+end)*end/2;//首项加末项乘以项数除以二
System.out.println(sum);//5050

递归的写法

static int recursion(int n){
if(n==1){//递归出口
return 1;
}else{
return n+recursion(n-1);
}
}

递归的核心其实就是一个if...else... 一个条件跳出,一个条件继续。

1.优点:使程序结构更清晰,更简洁,更容易让人理解,
2.缺点:使用递归调用时,**如果过多的调用容易造成java.lang.StackOverflowError即栈溢出和程序执行过慢。这是一个潜在Bug和影响程序执行效率问题,需要谨慎使用。**对于互联网这种以速度和效率来维护用户量,不得以用递归时,可以把处理的数据放入缓存,或者直接使用迭代等方式来解决。
3.规律:递归要有出口,不然成了死循环。解出递归的要点在于求出n-1,求出了n-1才能求解出n,这是为什么呢?

3.例题

3.1.题目:有一对兔子,从出生后第3个月起每个月都生一对兔子, 小兔子长到第四个月后每个月又生一对兔子, 假如兔子都不死,问每个月的兔子总数为多少?

分析,这个题目是著名的斐波那契数列:1,1,2,3,5,8,13,21....=Sn = Sn-1+Sn-2
规律是:从第三个数开始,每个数都是前两个数的和。

迭代方式

int month =10;
int sum[]= new int[month] ; //初始化月份数组
sum[0] = 1; //第一个月
sum[1] = 1; //第二个月
for(int i=2;i<=month-1;i++){
sum[i] = sum[i-1]+sum[i-2]; //第三个月等于前两个月之和
}
System.out.println("第"+month+"个月的兔子总数是:"+sum[month-1]);

递归方式

static int recursion(int i){
if( i == 1 || i == 2 ){
return 1;
}
else{
return recursion(i-1) + recursion(i-2);//第三项等于后两项之和
}
}

3.2 题目: 逆序排列字符单词,输入:I love java-->java love I

迭代方式

static String reverse(String str){
String[] strs = str.split(" ");
StringBuilder sb = new StringBuilder();
//倒叙遍历并拼接空格
for (int i = strs.length-1; i >= 0; i--) {
sb.append(strs[i]+" ");
}
return sb.toString();
}

递归方式

static String reverse(String s) {
int i = s.indexOf(" ");//搜索第一次出现" "的位置
if (s == null||i == -1) {
return s;
}
return reverse(s.substring(i + 1)) + " " + s.substring(0, i);//每次截取第一个单词放在最后拼接
}

3.3.题目:使用递归来统计字符串String str="hello"的长度,不能使用统计变量(只能用递归求解).

static int test1(String str){
if(null==str||str.equals("")){
return 0;
}
String[] strs = str.split("");
StringBuffer sb = new StringBuffer();//每次截取最后一个字母
for(int i = 0; i <= strs.length-2; i++)
{
sb. append(strs[i]);
}
return test1(sb.toString())+1;//每次递归调用+1
}

3.4 题目:实现二分查找算法.

二分查找,不断将数组进行对半分割,每次拿中间元素和goal进行比较(前提是数组元素的排序应该是递增或者递减)

public static void main(String[] args) {
int [] arr={1,2,5,6,8,9,12,64,78,90};//有序数组
System.out.println(test(arr,0,arr.length-1,8));//传入数组和数组长度,最后一个是要查找的值
}
//递归方式实现
public static int recursion(int [] arr,int low,int high,int value){
if(low>high){
return -1;
}
int mid=(low+high)/2;//求中间的值
if(value==arr[mid]){//如果相等,则找到该值,直接返回
return mid;
}else if(value<arr[mid]){//如果要找的值在中间值得左边,则下一次递归开始的右指针指向该次中间值-1
return recursion(arr,low,mid-1,value);
}else{////如果要找的值在中间值得右边,则下一次递归开始的左指针指向该次中间值+1
return recursion(arr,mid+1,high,value);
}
}
//循环方式实现
public static int test(int [] arr,int low,int high,int value){
int mid;
while(high>=low){
mid=(low+high)/2;
if(value<arr[mid]){
high=mid-1;
}else if(value>arr[mid]){
low=mid+1;
}else{
return mid;
}
}
return -1;//都没找到,则返回-1
}

3.5题目:求最大公约数和最小公倍数:

public static void main(String[] args) {
int x = 100;
int y = 18;
System.out.println("最大公约数:"+gcd(x,y));
System.out.println("最小公倍数:"+lcm(x,y));
}
//辗转相除法实现最大公约数
public static int gcd(int x, int y) {
if (y == 0){
return x;
}else{
return gcd(y, x % y);//x%y时,如果x<y则返回x,例:4%20=4 5%20=5,迭代之后会把小数放在后面,所以不用做交换
}
}
//最小公倍数 
public static int lcm(int p,int q){
int pq = p * q;
return pq / gcd(p,q);
}

3.6.题目:杨辉三角

        1 
       1 1 
      1 2 1 
     1 3 3 1 
    1 4 6 4 1 
   1 5 10 10 5 1 
 1 6 15 20 15 6 1 

分析:杨辉三角最本质的特征是,它的两条斜边都是由数字1组成的,而其余的数则是等于它肩上的两个数之和。

public static void main(String[] args) {
int n = 7;
for (int i = 1; i <= n; i++) {
//双层for循环是为了打印出三角形
for (int j = 1; j <= n-i; j++) {//每行前面的空格数
System.out.print(" ");
}
for (int j = 1; j <= i; j++) {
System.out.print(num(i,j)+" ");
}
System.out.println();//换行
}
}
public static int num(int x,int y){
if(y==1||y==x){
return 1;
}
return num(x-1,y-1)+num(x-1,y);//每一个数等于肩上两个数之和
}

3.7.题目:汉诺塔

接下来就到了递归的经典案例汉诺塔问题,本文就不对汉诺塔游戏规则进行讲解,如果以前没接触过汉诺塔,建议先玩玩汉诺塔游戏,总结一下游戏规律。

现在要把X柱上所有圆盘移动到Z
当移动3个圆盘


当移动6个圆盘

所以可以推出,当n个从x柱,经由y柱中转,移动到z柱(解出n层汉诺塔时),有:
当n=0时,
不用做任何操作
当n>0时,
首先,将n-1个盘子从x借助z移动到y
然后,将1个盘子从x移动到z
最后,将在中间y上的n-1个盘子借助x移动到z
为了解出n层汉诺塔,需要先使用n-1层汉诺塔的解法。

static int t=0;//最少移动次数
public static void main(String[] args) {
  hanio(3,"x","y","z");
  System.out.println(t);
}
static void hanio(int n ,String src,String mid,String dest){
  if(n==1){
    System.out.println(src+"-->"+dest);//移动过程
    t++;
  }else{
    hanio(n-1,src,dest,mid);//将n-1个盘子从x借助z移动到y
    hanio(1,src,"",dest);//因为中间柱子没用到,所以可以填""或者填mid,然后将最大的盘子从x直接移动到z
    hanio(n-1,mid,src,dest);//将在中间y柱上的n-1个盘子借助x移动到z
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,525评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,203评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,862评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,728评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,743评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,590评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,330评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,244评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,693评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,885评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,001评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,723评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,343评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,919评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,042评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,191评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,955评论 2 355

推荐阅读更多精彩内容

  • 递归与分治 一、斐波那契(Fibonacci)数列的递归实现 他讲的一个故事:如果说兔子在出生两个月后,就有繁殖能...
    我可能是个假开发阅读 1,044评论 0 10
  • 栈与递归 栈还有一个重要应用是在程序设计语言中实现递归。一个直接调用自己或通过一系列的调用语句间接的调用自己的函数...
    Mr_Bluyee阅读 3,109评论 0 1
  • 当一个问题规模比较大且不易求解的时候,就可以考虑将问题分成几个小的模块,逐一求解。分治思想和递归算是有亲兄弟的关系...
    NotFunGuy阅读 1,245评论 0 5
  • 前置文章:递归算法:www.jianshu.com/p/703069f3ba3f . 汉诺塔问题是来源于印度传...
    郎小凯阅读 768评论 0 1
  • 递归介绍 本来预算此章节是继续写快速排序的,然而编写快速排序往往是递归来写的,并且递归可能不是那么好理解,于是就有...
    Java3y阅读 16,095评论 7 20