日撸Java三百行(day16:递归)

目录

一、递归是什么

1.递归的原理

2.递归与for循环的联系

3.递归的应用场景

二、代码实现

1.0~N的累加实现

2.斐波那契数列实现

3.数据测试

4.完整的程序代码

总结


一、递归是什么

1.递归的原理

我们先来看一个小故事:

从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?讲的是:从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?讲的是:从前有座山,山上有座庙,庙里有个老和尚在给小和尚讲故事,讲的什么故事呢?……

相信大家都知道这个故事,它的特征就是在自身中又包含了自身,这其实就是递归的思想。如果在一个方法内部,又调用了该方法本身,那么就叫做递归(Recursion)。递归在解决复杂问题时非常有用,它可以将大问题分解为更小的子问题,这些子问题与原问题类似但却有着更小的规模,因此可以简化问题,实现更高效的算法。

简言之,递归就是方法或者函数调用自身,递归的形式有以下两种:

  • 直接递归:方法自己调用自己
  • 间接递归:方法调用其他方法,其他方法又回调方法自己

不过,使用递归时有两个必要条件需要满足:

  • 分解得到的子问题必须与原问题解法相同
  • 必须有一个明确的结束条件(即递归出口),以防止无限循环

2.递归与for循环的联系

根据递归的原理,可以发现一维的for循环,都可以用递归实现,只需要每次调用递归函数时改变参数(要求当前参数与上一次调用时使用的参数存在逻辑关联)即可。

下面我们分别用for循环和递归实现1~100的累加,代码如下:

  • for循环
int tempResult = 0;

for(int i = 1; i <= 100; i++) {
   tempResult += i;
} // of for i

System.out.println("Result by for is " + tempResult);

  • 递归
public static int Recursion(int n) {
   if(n == 1) {
      return 1;
   } // of if
		
   return Recursion(n - 1) + n;
} // of Recursion

public static void main(String[] args) {
   System.out.println("Rssult by recursion is " + Recursion(100));
} // of main

 

可以看到,这两种方法得到的运行结果是完全一样的。在使用递归时,我们加入了一个if语句(当输入的参数n = 1时,直接返回1,不再继续进行调用),这其实就是我们之前提到的明确的结束条件,也就是递归的出口。

其实,对于类似累加的递推操作,都可以用如上的递归函数作为模板。

3.递归的应用场景

递归在很多问题中都有应用,特别是那些可以被分解为更小规模的子问题的情况,常见的递归应用场景如下:

  • 数学问题,比如累加、阶乘、斐波那契数列等
  • 数据结构操作,比如遍历树的节点、链表反转等
  • 搜索和回溯算法,比如深度优先搜索、回溯法等
  • 分治法,比如归并排序、快速排序等

二、代码实现

1.0~N的累加实现

在上面,我们以1~100的累加为例,学习了对于累加这种递推形式应该怎样构造递归函数,这里直接套用即可,代码如下:

    /**
	 *********************
	 * Sum to N. No loop, however a stack is used.
	 * 
	 * @param paraN The given value.
	 * @return The sum.
	 *********************
	 */
	public static int sumToN(int paraN) {
		if(paraN <= 0) {
            // Basis.
			return 0;
		} // of if

		return sumToN(paraN - 1) + paraN;
	} // of sumToN

显然,这里使用到的递归方程为 f(N) = f(N - 1) + N,而递归出口用if语句控制,当 paraN <= 0 时,直接返回0,不再继续进行函数调用,递归也就结束了。

2.斐波那契数列实现

我们先来了解一下斐波那契数列的基本概念:

斐波那契数列(Fibonacci sequence)又称黄金分割数列或兔子数列,其数值为:1,1,2,3,5,8,13,21,34,…即从该数列的第三项开始,每一项都等于前两项之和。其递推方式可以概括为F(0) = 0,F(1) = 1,F(n) = F(n-1) + F(n-2)(n≥2,n∈N*)。

接下来,就可以进行代码模拟了。根据定义,很容易就可以得到递推方程为 f(N) = f(N - 1) + N,然后,需要注意这里应该有两个递归出口,一个是 paraN <= 0 时,直接返回0,另一个是 paraN = 1时,直接返回1。因此,创建方法如下:

    /**
	 *********************
	 * Fibonacci sequence.
	 * 
	 * @param paraN The given value.
	 * @return The sum.
	 *********************
	 */
	public static int fibonacci(int paraN) {
		if(paraN <= 0) {
            // Negative values are invalid. Index 0 corresponds to the first element 0.
			return 0;
		} // of if
		
		if(paraN == 1) {
            // Basis.
			return 1;
		} // of if
		
		return fibonacci(paraN - 1) + fibonacci(paraN - 2);
	} // of fibonacci

3.数据测试

接着,我们照例进行一些数据测试,分别是0~5的累加、0~ -1的累加以及斐波那契数列前9项的计算,代码如下:

    /**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String[] args) {
		int tempValue = 5;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));
		tempValue = -1;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));
		
		for(int i = 0; i < 10; i++) {
			System.out.println("fibonacci " + i + " : " + fibonacci(i));
		} // of for i
	} // of main

4.完整的程序代码

package datastructure;

/**
 * Recursion. A method can (directly or indirectly) invoke itself. The system
 * automatically creates a stack for it.
 *
 *@auther Xin Lin 3101540094@qq.com.
 */

public class Recursion {

	/**
	 *********************
	 * Sum to N. No loop, however a stack is used.
	 * 
	 * @param paraN The given value.
	 * @return The sum.
	 *********************
	 */
	public static int sumToN(int paraN) {
		if(paraN <= 0) {
            // Basis.
			return 0;
		} // of if

		return sumToN(paraN - 1) + paraN;
	} // of sumToN
	
	/**
	 *********************
	 * Fibonacci sequence.
	 * 
	 * @param paraN The given value.
	 * @return The sum.
	 *********************
	 */
	public static int fibonacci(int paraN) {
		if(paraN <= 0) {
            // Negative values are invalid. Index 0 corresponds to the first element 0.
			return 0;
		} // of if
		
		if(paraN == 1) {
            // Basis.
			return 1;
		} // of if
		
		return fibonacci(paraN - 1) + fibonacci(paraN - 2);
	} // of fibonacci
	
	/**
	 *********************
	 * The entrance of the program.
	 * 
	 * @param args Not used now.
	 *********************
	 */
	public static void main(String[] args) {
		int tempValue = 5;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));
		tempValue = -1;
		System.out.println("0 sum to " + tempValue + " = " + sumToN(tempValue));
		
		for(int i = 0; i < 10; i++) {
			System.out.println("fibonacci " + i + " : " + fibonacci(i));
		} // of for i
	} // of main
} // of class Recursion

运行结果

总结

递归是一种强大且常用的编程技巧,在java中经常被使用,它可以将复杂的问题分解为更小规模的子问题,简化代码逻辑;同时能够直观地表达问题的解决思路,提高代码的可读性;而且在某些算法中还能实现更高效的解决方法。不过,递归也存在一些不足,因为递归的函数调用本质上是栈的使用过程,所以如果递归中没有明确的结束条件,那么就非常容易导致用栈过深,造成栈溢出错误,同时递归调用需要创建多个栈帧,对系统资源有一定的消耗。

最后,递归解决问题的思想非常值得掌握,它并不按照先后顺序从头开始,而是忽略个别细节,总结出一般的递推关系,然后利用这个递推关系将分解成的子问题一一连接起来。

  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值