递归算法的讲解

article/2025/10/7 4:14:25

原作者:书呆子Rico 《递归的内涵与经典应用》 http://my.csdn.net/justloveyou_

摘要:

  大师 L. Peter Deutsch 说过:To Iterate is Human, to Recurse, Divine.中文译为:人理解迭代,神理解递归。毋庸置疑地,递归确实是一个奇妙的思维方式。对一些简单的递归问题,我们总是惊叹于递归描述问题的能力和编写代码的简洁,但要想真正领悟递归的精髓、灵活地运用递归思想来解决问题却并不是一件容易的事情。本文剖析了递归的思想内涵,分析了递归与循环的联系与区别,给出了递归的应用场景和一些典型应用,并利用递归和非递归的方式解决了包括阶乘、斐波那契数列、汉诺塔、杨辉三角的存取、字符串回文判断、字符串全排列、二分查找、树的深度求解在内的八个经典问题。

一. 引子

   大师 L. Peter Deutsch 说过:To Iterate is Human, to Recurse, Divine.中文译为:人理解迭代,神理解递归。毋庸置疑地,递归确实是一个奇妙的思维方式。对一些简单的递归问题,我们总是惊叹于递归描述问题的能力和编写代码的简洁,但要想真正领悟递归的精髓、灵活地运用递归思想来解决问题却并不是一件容易的事情。在正式介绍递归之前,我们首先引用知乎用户李继刚(https://www.zhihu.com/question/20507130/answer/15551917)对递归和循环的生动解释:

   递归:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。

   循环:你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门(若前面两扇门都一样,那么这扇门和前两扇门也一样;如果第二扇门比第一扇门小,那么这扇门也比第二扇门小,你继续打开这扇门,一直这样继续下去直到打开所有的门。但是,入口处的人始终等不到你回去告诉他答案。

   上面的比喻形象地阐述了递归与循环的内涵,那么我们来思考以下几个问题:

什么是递归呢? 
递归的精髓(思想)是什么? 
递归和循环的区别是什么? 
什么时候该用递归? 
使用递归需要注意哪些问题? 
递归思想解决了哪些经典的问题? 
这些问题正是笔者准备在本文中详细阐述的问题。

二. 递归的内涵

1、定义 (什么是递归?)

   在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:递 和 归,这正是递归思想的精华所在。

2、递归思想的内涵(递归的精髓是什么?)

   正如上面所描述的场景,递归就是有去(递去)有回(归来),如下图所示。“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。

更直接地说,递归的基本思想就是把规模大的问题转化为规模小的相似的子问题来解决。特别地,在函数实现时,因为解决大问题的方法和解决小问题的方法往往是同一个方法,所以就产生了函数调用它自身的情况,这也正是递归的定义所在。格外重要的是,这个解决问题的函数必须有明确的结束条件,否则就会导致无限递归的情况。

3、用归纳法来理解递归

   数学都不差的我们,第一反应就是递归在数学上的模型是什么,毕竟我们对于问题进行数学建模比起代码建模拿手多了。观察递归,我们会发现,递归的数学模型其实就是 数学归纳法,这个在高中的数列里面是最常用的了,下面回忆一下数学归纳法。

而递归和我们的思维方式正好相反。

那我们怎么判断这个递归计算是否是正确的呢?Paul Graham 提到一种方法,如下:

如果下面这两点是成立的,我们就知道这个递归对于所有的 n 都是正确的。

  1. 当 n=0,1 时,结果正确;

  2. 假设递归对于 n 是正确的,同时对于 n+1 也正确。

这种方法很像数学归纳法,也是递归正确的思考方式,上述的第 1 点称为基本情况,第 2 点称为通用情况。

在递归中,我们通常把第 1 点称为终止条件,因为这样更容易理解,其作用就是终止递归,防止递归无限地运行下去。

   数学归纳法适用于将解决的原问题转化为解决它的子问题,而它的子问题又变成子问题的子问题,而且我们发现这些问题其实都是一个模型,也就是说存在相同的逻辑归纳处理项。当然有一个是例外的,也就是归纳结束的那一个处理方法不适用于我们的归纳处理项,当然也不能适用,否则我们就无穷归纳了。总的来说,归纳法主要包含以下三个关键要素:

步进表达式:问题蜕变成子问题的表达式 
结束条件:什么时候可以不再使用步进表达式 
直接求解表达式:在结束条件下能够直接计算返回值的表达式 
事实上,这也正是某些数学中的数列问题在利用编程的方式去解决时可以使用递归的原因,比如著名的斐波那契数列问题。

4、递归的三要素

   在我们了解了递归的基本思想及其数学模型之后,我们如何才能写出一个漂亮的递归程序呢?笔者认为主要是把握好如下三个方面:

1、明确递归终止条件;

2、给出递归终止时的处理办法;

3、提取重复的逻辑,缩小问题规模。

 

1). 明确递归终止条件

   我们知道,递归就是有去有回,既然这样,那么必然应该有一个明确的临界点,程序一旦到达了这个临界点,就不用继续往下递去而是开始实实在在的归来。换句话说,该临界点就是一种简单情境,可以防止无限递归。

2). 给出递归终止时的处理办法

   我们刚刚说到,在递归的临界点存在一种简单情境,在这种简单情境下,我们应该直接给出问题的解决方案。一般地,在这种情境下,问题的解决方案是直观的、容易的。

3). 提取重复的逻辑,缩小问题规模*

   我们在阐述递归思想内涵时谈到,递归问题必须可以分解为若干个规模较小、与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决。从程序实现的角度而言,我们需要抽象出一个干净利落的重复的逻辑,以便使用相同的方式解决子问题。

5、递归算法的编程模型

   在我们明确递归算法设计三要素后,接下来就需要着手开始编写具体的算法了。在编写算法时,不失一般性,我们给出两种典型的递归算法设计模型,如下所示。

模型一: 在递去的过程中解决问题 

function recursion(大规模){if (end_condition){      // 明确的递归终止条件end;   // 简单情景}else{            // 在将问题转换为子问题的每一步,解决该步中剩余部分的问题solve;                // 递去recursion(小规模);     // 递到最深处后,不断地归来}
}

模型二: 在归来的过程中解决问题

function recursion(大规模){if (end_condition){      // 明确的递归终止条件end;   // 简单情景}else{            // 先将问题全部描述展开,再由尽头“返回”依次解决每步中剩余部分的问题recursion(小规模);     // 递去solve;                // 归来}
}

6、递归的应用场景

   在我们实际学习工作中,递归算法一般用于解决三类问题:

   (1). 问题的定义是按递归定义的(Fibonacci函数,阶乘,…);

   (2). 问题的解法是递归的(有些问题只能使用递归方法来解决,例如,汉诺塔问题,…);

   (3). 数据结构是递归的(链表、树等的操作,包括树的遍历,树的深度,…)。

  在下文我们将给出递归算法的一些经典应用案例,这些案例基本都属于第三种类型问题的范畴。

三. 递归与循环

   递归与循环是两种不同的解决问题的典型思路。递归通常很直白地描述了一个问题的求解过程,因此也是最容易被想到解决方式。循环其实和递归具有相同的特性,即做重复任务,但有时使用循环的算法并不会那么清晰地描述解决问题步骤。单从算法设计上看,递归和循环并无优劣之别。然而,在实际开发中,因为函数调用的开销,递归常常会带来性能问题,特别是在求解规模不确定的情况下;而循环因为没有函数调用开销,所以效率会比递归高。递归求解方式和循环求解方式往往可以互换,也就是说,如果用到递归的地方可以很方便使用循环替换,而不影响程序的阅读,那么替换成循环往往是好的。问题的递归实现转换成非递归实现一般需要两步工作:

   (1). 自己建立“堆栈(一些局部变量)”来保存这些内容以便代替系统栈,比如树的三种非递归遍历方式;

   (2). 把对递归的调用转变为对循环处理。

   特别地,在下文中我们将给出递归算法的一些经典应用案例,对于这些案例的实现,我们一般会给出递归和非递归两种解决方案,以便读者体会。

四. 经典递归问题实战

  1. 第一类问题:问题的定义是按递归定义的 
  2. (1). 阶乘

     

/*** Title: 阶乘的实现 * Description:*      递归解法*      非递归解法* @author rico*/
public class Factorial {/**     * @description 阶乘的递归实现* @author rico       * @created 2017年5月10日 下午8:45:48     * @param n* @return     */public static long f(int n){if(n == 1)   // 递归终止条件 return 1;    // 简单情景return n*f(n-1);  // 相同重复逻辑,缩小问题的规模}--------------------------------我是分割线-------------------------------------/**     * @description 阶乘的非递归实现* @author rico       * @created 2017年5月10日 下午8:46:43     * @param n* @return     */public static long f_loop(int n) {long result = n;while (n > 1) {n--;result = result * n;}return result;}
}

(2). 斐波纳契数列

/** 
* Title: 斐波纳契数列 
* 
* Description: 斐波纳契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、…… 
* 在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=F(n-1)+F(n-2)(n>=2,n∈N*)。 
* 
* 两种递归解法:经典解法和优化解法 
* 两种非递归解法:递推法和数组法  
* @author rico*/
public class FibonacciSequence {/*** @description 经典递归法求解* * 斐波那契数列如下:* *  1,1,2,3,5,8,13,21,34,...* * *那么,计算fib(5)时,需要计算1次fib(4),2次fib(3),3次fib(2),调用了2次fib(1)*,即:* *  fib(5) = fib(4) + fib(3)*  *  fib(4) = fib(3) + fib(2) ;fib(3) = fib(2) + fib(1)*  *  fib(3) = fib(2) + fib(1)*  * 这里面包含了许多重复计算,而实际上我们只需计算fib(4)、fib(3)、fib(2)和fib(1)各一次即可,* 后面的optimizeFibonacci函数进行了优化,使时间复杂度降到了O(n).* * @author rico* @created 2017年5月10日 下午12:00:42* @param n* @return*/public static int fibonacci(int n) {if (n == 1 || n == 2) {     // 递归终止条件return 1;       // 简单情景}return fibonacci(n - 1) + fibonacci(n - 2); // 相同重复逻辑,缩小问题的规模}

(3). 杨辉三角的取值

    /**     * @description 递归获取杨辉三角指定行、列(从0开始)的值*              注意:与是否创建杨辉三角无关    * @author rico * @x  指定行* @y  指定列    *//*** Title: 杨辉三角形又称Pascal三角形,它的第i+1行是(a+b)i的展开式的系数。* 它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。* * 例如,下面给出了杨辉三角形的前4行: *    1 *   1 1*  1 2 1* 1 3 3 1* @description 递归获取杨辉三角指定行、列(从0开始)的值*              注意:与是否创建杨辉三角无关* @author rico * @x  指定行* @y  指定列  */public static int getValue(int x, int y) {if(y <= x && y >= 0){if(y == 0 || x == y){   // 递归终止条件return 1; }else{ // 递归调用,缩小问题的规模return getValue(x-1, y-1) + getValue(x-1, y); }}return -1;} 
}

         (1). 汉诺塔问题

  1. 第二类问题:问题解法按递归算法实现

    

/** 
* Title: 汉诺塔问题 
* Description:古代有一个梵塔,塔内有三个座A、B、C,A座上有64个盘子,盘子大小不等,大的在下,小的在上。 
* 有一个和尚想把这64个盘子从A座移到C座,但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘子始终保持大盘在下, 
* 小盘在上。在移动过程中可以利用B座。要求输入层数,运算后输出每步是如何移动的。 
* 
* @author rico*/
public class HanoiTower {/**     * @description 在程序中,我们把最上面的盘子称为第一个盘子,把最下面的盘子称为第N个盘子* @author rico       * @param level:盘子的个数* @param from 盘子的初始地址* @param inter 转移盘子时用于中转* @param to 盘子的目的地址*/public static void moveDish(int level, char from, char inter, char to) {if (level == 1) { // 递归终止条件System.out.println("从" + from + " 移动盘子" + level + " 号到" + to);} else {// 递归调用:将level-1个盘子从from移到inter(不是一次性移动,每次只能移动一个盘子,其中to用于周转)moveDish(level - 1, from, to, inter); // 递归调用,缩小问题的规模// 将第level个盘子从A座移到C座System.out.println("从" + from + " 移动盘子" + level + " 号到" + to); // 递归调用:将level-1个盘子从inter移到to,from 用于周转moveDish(level - 1, inter, from, to); // 递归调用,缩小问题的规模}}public static void main(String[] args) {int nDisks = 30;moveDish(nDisks, 'A', 'B', 'C');}
  1. 第三类问题:数据的结构是按递归定义的

       (1). 二叉树深度 

 

/** 
* Title: 递归求解二叉树的深度 
* Description: 
* @author rico 
* @created 2017年5月8日 下午6:34:50 
*/
public class BinaryTreeDepth {/**     * @description 返回二叉数的深度* @author rico       * @param t* @return     */public static int getTreeDepth(Tree t) {// 树为空if (t == null) // 递归终止条件return 0;int left = getTreeDepth(t.left); // 递归求左子树深度,缩小问题的规模int right = getTreeDepth(t.left); // 递归求右子树深度,缩小问题的规模return left > right ? left + 1 : right + 1;}
}

 


http://chatgpt.dhexx.cn/article/t0HJQotU.shtml

相关文章

递归算法经典例题及详解

有句话叫递归算法只可意会不可言传&#xff0c;我也领略了&#xff0c;感觉递归算法好神奇&#xff0c;不知不觉的就把工作做完了! 下面这道题也是小编从力扣上看来得&#xff0c;但是关于它是怎样递归的是小编自己想的哦❤️❤️如果有不足&#xff0c;希望大家多多指正&#…

递归算法及经典递归例子代码实现

一、什么叫做递归&#xff1f; 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法&#xff1b; 递归函数就是直接或间接调用自身的函数&#xff0c;也就是自身调用自己&#xff1b; 二、一般什么时候使用递归&#xff1f; 递归时常用的编程技术&#xff0c;其基本…

递归算法(图文详解)

递归算法 一、算法概述 递归算法是一种直接或者间接调用自身函数或者方法的算法。说简单了就是程序自身的调用。二、算法实质 递归算法就是将原问题不断分解为规模缩小的子问题&#xff0c;然后递归调用方法来表示 问题的解。&#xff08;用同一个方法去解决规模不同的问题&am…

递归算法及经典例题详解

大部分人在学习编程时接触的第一个算法应该就是递归了&#xff0c;递归的思想其实很好理解&#xff0c;就是将一个问题拆分为若干个与本身相似的子问题&#xff0c;通过不断调用自身来求解。 但很多新手在实际操作中却很难正确使用到递归&#xff0c;有时面对问题还会有种无从…

几道经典递归算法案例

一&#xff09;递归介绍 定义&#xff1a; 1、在函数体中直接或间接的调用自身的一种方法。 2、必须要有边界值&#xff0c;也就是停止的条件。 头递归&#xff1a;函数调用时不是传递本次计算的结果&#xff0c;而是把当前的调用状态传递&#xff0c;相当于要一直记录上一…

【函数递归】简单递归的5个经典例子,你都会吗?

&#x1f9f8;&#x1f9f8;&#x1f9f8;各位巨佬大家好&#xff0c;我是猪皮兄弟&#x1f9f8;&#x1f9f8;&#x1f9f8; 今天和大家谈谈简单递归&#x1f973;&#x1f973;&#x1f973; &#x1f692;什么是递归 递归的定义&#xff1a; 递归是一种解决问题的有效方…

四个超好用的优质资源搜索网站,海量优质资源等你发现!

在网上找资源的时候总找不到满意的优质资源&#xff1f;今天小编把办公室大佬珍藏多年的四个超好用优质资源搜索网站分享给你&#xff0c;只要你想找&#xff0c;没有找不到的资源&#xff01; 一&#xff0c;学习资料库 学习资料库中有大量的免费学习资料&#xff0c;学习资料…

珍藏多年的技术资源搜索网站——程序员必备

程序员经常需要找一些技术书籍和相关文档&#xff0c;但是通过百度查找往往都是需要各种积分才能够下载&#xff0c;笔者平时的学习中积累几个搜索工具网站&#xff0c;基本上所有需要的技术文档&#xff0c;经典书籍&#xff0c;学习资料&#xff0c;学习视频等等都可以在下列…

在百度上搜不到的资源是在哪找的?就在这些强大的资源搜索网站呀

都说又不懂的问“度娘”最快&#xff0c;但是也有一些东西是在“度娘”哪里也找不到的。那些在百度上搜不到的资源是在哪找的呢&#xff1f;就在这些强大的资源搜索网站呀~ 1.茶杯狐 茶杯狐&#xff0c;它的资源搜索功能很成熟&#xff0c;这里收集了海量的资源可以免费下载&a…

黑科技之资源搜索网站

欢迎加入BIM行业开发交流1群 群号:711844216 一、背景 大家经常会有搜索视频&#xff0c;音乐&#xff0c;PDF&#xff0c;APP等资源的需求&#xff0c;然而通过现有搜索引擎不是很方便找到自己想要的东西&#xff0c;这里笔者推荐几个不错的免费网站&#xff0c;基本上用起来…

超好用的云盘资源搜索网站

超好用的云盘资源搜索网站 云盘资源搜索云云搜索搜百度盘盘搜搜万盘搜索爱挖盘盘搜我的盘 云盘资源搜索 想要的资源轻轻松松一键搞定&#xff0c;让工作生活更轻松。 注*本分享仅供学习参考 云云搜索 http://www.yunyunso.com 搜百度盘 http://www.sobaidupan.com 盘搜…

网盘资源搜索网站

本篇博客总结了百度网盘常用的资源搜索网站。 1.盘搜搜 盘搜搜支持百度云搜索、115网盘、360云盘、华为网盘、新浪微盘等搜索服务&#xff0c;是您工作、学习、娱乐的网盘搜索神器 2.如风搜 如风搜&#xff0c;全网网盘搜索工具。是您工作、学习、娱乐的好助手。找好资源&…

6个超级实用的免费网盘搜索网站分享

因为各种原因&#xff0c;多数网盘搜索网站“寿命”都很短&#xff0c;也有很多百度网盘搜索站点都提高了使用门槛&#xff08;比如付费才能使用&#xff09;。 分享几个自己认为搜索的资料蛮全&#xff0c;到现在&#xff08;2021年&#xff09;为止还能使用的网盘搜索网站。…

我珍藏很久的网盘资源搜索网站和下载神器

摘要&#xff1a;分享几个网盘资源搜索网站和下载神器。 这是「每周分享」的第 17 期。最近有不少新朋友关注&#xff0c;所在再介绍一下这个栏目&#xff0c;每个周末分享一篇 Python 以外的文章&#xff0c;主题涉及&#xff1a;软件、App、PPT、Ps 等几个方面。往期内容你可…

强烈推荐,7个资源搜索网站,从此告别资源付费

前言 周末软件测试自学群很多小伙伴问我&#xff1a;你有啥好的学习资源或者网站没&#xff1f;分享一下可以吗&#xff1f;虽然在国庆期间我整理过&#xff0c;但是趁着周末又给大家整理了一波&#xff0c;完善了不少学习资源&#xff0c;走起~~ 脚本之家 资源很多&#xf…

12款精品网盘资源搜索网站,只有你想不到没有它搜不到的

目录 1.前言 2.网站介绍 3.使用评价 12款精品网盘资源搜索网站&#xff0c;只有你想不到没有它搜不到的 1.前言 今天呢给大家分享几款网盘资源搜索网站&#xff0c;比如盘搜搜、盘盘搜、茶杯狐等&#xff0c;都是非常老牌的网盘资源搜索网站&#xff0c;里面资源丰富且全…

7个资源丰富到爆的搜索网站,没有你找不到的资源!

闲话就不多说了&#xff0c;直接上干货&#xff0c;希望能够帮助到大家&#xff01; IMDB IMDB是互联网电影数据库网站。它包含了所有的电影、电视的内容。如果说豆瓣是国内经常使用的&#xff0c;那么在豆瓣上看不到的东西&#xff0c;使用IMDB就可以查到了。可以使用它来查询…

3个超实用的资源搜索网站,有了它们,再也没有你找不到的资源!

还在为找不到自己需要的资源而烦恼&#xff1f;别烦恼啦~快来看看这3个超实用的资源搜索网站&#xff0c;有了它们&#xff0c;再也没有你找不到的资源&#xff01; 1.BT搜 BT搜&#xff0c;这个网站虽然看起来比较简单&#xff0c;但是却有很多非常实用的资源&#xff0c;没有…

还在用百度找资源?试试这3个顶级资源搜索网站,没有找不到的!

资源搜索是工作和学习的日常&#xff0c;相信很多人都喜欢用百度去搜索&#xff0c;虽然百度很强大&#xff0c;但是毕竟资源有限&#xff0c;今天再给大家分享3个顶级资源搜索网站&#xff0c;视频、音乐、图片等应有尽有&#xff0c;没有你找不到的哦。 1.虫部落 一个解决稀…

使用阿里云开放搜索服务快速搭建资源搜索网站

下面我们就一步一步来搭建这个简单的资源搜索网站 一、搭建前的一些准备和分析 资源搜索网站有如下几个关键点&#xff1a; 1、原始数据 没有个几百万条初始搜索数据&#xff0c;都不好意思和别人说是做资源站的&#xff0c;在这个案例里面&#xff0c;我们采用了simplecd官…