首页 > 编程语言 >如何理解递归算法?

如何理解递归算法?

时间:2024-03-21 09:02:17浏览次数:34  
标签:pre 结点 curr 递归 next 算法 理解 单步

 

首先说说递归思想,我认为可以从以下三点进行把握:

  1. 将大问题分解为有限个子问题;
  2. 每个子问题的求解方式相同;
  3. 存在已知的最小子问题,作为“归”的条件。

 

  一句话解释:递归思想是将大问题分解为数个求解方式相同的子问题,且该问题具有已知的最小子问题。

 

另外,递归是分为两个部分:“递”和“归”,这不是废话。在递归算法中,我们可以在“递”的时候处理问题,也可以在“归”的时候处理问题,前者更高效一点,但问题不大。

  要想把握住“递”,就是理解函数下一步调用自己干了啥?

  要想把握住“归”,就是理解函数的返回条件是什么?是那个已知的最小子问题。

 

然后说说如何理解某个递归算法:

  1. 一定要从宏观理解,即理解此函数总体而言是干什么的,究竟是处理什么“大问题”的,切忌死扣一大堆单步理解。
  2. 在递归算法中,大问题被分解为数个求解方式相同的子问题,这个求解方式体现在该递归函数单步做了什么事,因为递归单步只处理一个子问题。换句话说,所谓单步操作,实际上就是每次对子问题的求解
  3. 找到递归函数的递推方程,或基本思想;
  4. 最后把一大堆单步处理按照递推思想,直接像多米诺骨牌一样递推就好了!
  5. 注意“归”的条件

 

下举例说明:

 

宏观来看,这是一个反转单链表的函数:(注:结构体在文章末)

 1 /**
 2  * @param: *curr是当前单链表表头结点,*pre是*curr的前一个结点
 3 */
 4 LinkedList reverseList(LNode *curr, LNode *pre = nullptr) {
 5     // 最小子问题:若当前链表头结点指针为空,则返回pre指针即可
 6     if (curr == nullptr) {
 7         return pre;
 8     }
 9 
10     // 当前操作:反转curr与pre
11     LNode *next = curr->next;   // 先保存一下curr->next, 它是待会下一次反转的头结点
12     curr->next = pre;
13 
14     return reverseList(next, curr);    // 向后递推
15 }

 

但可能你还是不太好把握,下面我来详细说说:

  1. 首先我们来看看该递归函数的单步操作,结合函数变量名我们可以发现:它的单步操作核心是反转curr与pre,即把curr->next指向pre。
  2. 然后函数就接着调用自身,注意传递的参数变了,原来是curr的地方传next, 而原本是pre的地方传了curr。
  3. 这个next是什么呢?在当前函数中,next是curr->next, 哦原来next是单链表的第二个结点。所以接下来的调用就是反转next与curr。
  4. 这样的单步操作一步步的像多米诺骨牌一样传递下去,直到curr变成nullptr,此时已经递到单链表末尾了,该返回了,即触发了“归”的条件。

 

结合这个例子我们再来看看如何理解递归算法:

  1. 找到单步操作:在此例中是反转curr与pre;
  2. “递”了啥?即下一步干了啥:在此例中是反转next与pre
  3. 啥时候“归”的?哦,是当curr为nullptr的时候,实际上就是当前链表为空的时候。
  4. 怎么像多米诺骨牌一样?此例中,每次反转curr与pre,下一次反转curr->next与curr,一直一直递推下去到单链表末尾,也就把全部结点反转了!

 

 

 

 

注释:

①    此例是尾递归,还有另一种“当前结果依赖于之后结果”的递归没讲,下次再写;

②    单链表结构体如下

1 #define ElemType int
2 typedef struct LNode {
3     ElemType data;
4     LNode *next;
5 } LNode, *LinkedList;

③    如果你想用此例反转带头结点的单链表,也很简单,外边再套个娃就行。简单来说就是:一个反转带头结点的单链表的函数,里边调用了反转头结点之后单链表的函数,最后还是返回头结点地址即可。

④    请期待下一篇《如何写递归算法》

标签:pre,结点,curr,递归,next,算法,理解,单步
From: https://www.cnblogs.com/hk416hasu/p/18086563

相关文章

  • 【数据结构和算法初阶(C语言)】二叉树的顺序结构--堆的实现/堆排序/topk问题详解---二
     目录 ​编辑1.二叉树的顺序结构及实现1.1二叉树的顺序结构2堆的概念及结构3堆的实现3.1堆的代码定义3.2堆插入数据3.3打印堆数据3.4堆的数据的删除3.5获取根部数据3.6判断堆是否为空3.7堆的销毁 4.建堆以及堆排序 4.1堆排序---是一种选择排序4.2升......
  • 深入理解 SpringAOP(一):AOP 组件概述
    概述spring-aop模块是Spring框架中最重要的组件之一,它为我们提供了强大的AOP功能,并为其他扩展功能(如声明式事务、声明式异步处理等)提供了支持。在本文中,我们将深入探讨SpringAOP的源码,从代理对象的创建开始,揭示SpringAOP的运行机制。首先,在阅读这篇文章前,请先确保对Sp......
  • 深入理解 SpringAOP(二):AOP的执行流程
    概述在之前的文章中,我们已经对SpringAOP的关键组件进行了描述,并且了解了其基本操作和流程。在本文中,我们将进一步深入源码,揭示SpringAOP的内部实现细节,理解其运行机制的每个环节,包括切面的织入方式、代理对象的创建过程、连接点的定位与匹配等。通过对完整运行流程的深入研究......
  • 深入理解 SpringMVC
    前言SpringMVC可以说是我们日常开发中最依赖的Spring组件了,它基于Servlet容器实现,允许我们通过注解的方式开发Web程序。在本篇文章,将深入SpringMVC源码,梳理SpringMVC对Web请求处理流程,弄懂相关核心组件的原理,最终做到在使用的时候知其然也知其所以然。一、接受并分......
  • 最短路径算法
    原文链接:https://blog.csdn.net/xiaoxi_hahaha/article/details/110257368已知起始结点,求最短路径的问题。适合使用Dijkstra算法。迪杰斯特拉算法是由荷兰计算机科学家在1956年发现的算法,此算法使用类似广度优先搜索的方法解决了带权图的单源最短路径问题。它是一个贪心算法。全......
  • 代码随想录第14天|二叉树的递归遍历
    二叉树的理论基础代码随想录(programmercarl.com)关于二叉树,你该了解这些!|二叉树理论基础一网打尽,二叉树的种类、二叉树的存储方式、二叉树节点定义、二叉树的遍历顺序_哔哩哔哩_bilibili二叉搜索树:二叉搜索树是一个有序树。左子树不为空,则左子树上所有节点的值均小于根......
  • 代码学习第24天----回溯算法
    随想录日记part24time:time:time:2024.03.10主......
  • Java递归拷贝文件夹
    importjava.io.*;importjava.util.Scanner;publicclassDemo{publicstaticvoidmain(String[]args){FilesrcDirFile=getDirFile("输入源文件夹路径");FiledestDirFile=getDirFile("输入目标路径");if(srcDirFile.e......
  • Java递归删除文件夹
    importjava.io.File;importjava.util.Scanner;publicclassDemo{publicstaticvoidmain(String[]args){FiledirFile=getDirFile();delDirFiles(dirFile);}/***递归删除文件夹里所有文件*/privatestaticvoi......
  • Java递归计算一个文件夹所有文件大小
    importjava.io.File;importjava.util.Scanner;publicclassDemo1{publicstaticvoidmain(String[]args){FiledirFile=getDirFile();System.out.println(countDirFile(dirFile));}/***计算文件夹大小*/public......