首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

数据结构与算法 --- 递归(二)

探究产生堆栈溢出的原因 函数调用采用「函数调用栈」来保存当前“快照”(局部变量,返回地址等)。函数调用栈是内存中开辟的一块存储空间,它被组织成“栈”这种数据结构,数据先进后出。...递归的过程包含大量的函数调用,如果递归求解的数据规模很大,函数调用层次很深,那么函数调用栈中的数据(栈帧)会越来越多,而函数调用栈空间一般不大,堆栈空间不足以存储所有的调用信息,从而导致堆栈溢出。...在 Factorial(n - 1) 执行完成之后,返回结果(假设是 result ),编译器就从函数调用栈中取出之前保存的栈帧(局部变量 n 和Factorial(n - 1) 的返回地址)。...上文说到,函数调用栈中保存局部变量和返回地址,而对于尾递归代码,递归代码出现在最后一行中,返回之后不需要继续往下执行,因此,返回地址可以不用保存;而局部变量 n 也被移动到新的函数 Factorial(...但是在实际开发过程中,尾递归其实并没有太大作用,不能期望它来规避递归导致的堆栈溢出问题,主要表现在: 并不是所有编程语言都支持尾递归优化 并不是所有的递归都可以改成尾递归 能改成尾递归的代码也就都可以改成迭代方式

18310

数据结构与算法:递归算法

对于可以用其相似的子任务来定义的任务,递归是最好的解决方案之一。例如:数字的阶乘。 递归的性质 使用不同的输入多次执行相同的操作。 在每一步中,我们都会尝试较小的输入来使问题更小。...递归函数如何存储在内存中? 递归使用更多内存,因为递归函数会在每次递归调用时将值添加到堆栈中,并将值保留在那里,直到调用完成。递归函数使用 LIFO(后进先出)结构,就像堆栈数据结构一样。...为什么递归会出现Stack Overflow错误? 如果未达到或未定义基本情况,则可能会出现堆栈溢出问题。让我们举个例子来理解这一点。...如果堆栈上的内存被这些函数耗尽,就会导致堆栈溢出错误。 直接递归和间接递归有什么区别? 如果函数 fun 调用相同的函数 fun,则该函数被称为直接递归。...在语句 2 中,调用printFun(2),为 **printFun(2)**分配内存,并将局部变量 test 初始化为 2,并将语句 1 到 4 压入堆栈。

19410
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    C语言函数:编程世界的魔法钥匙(2)-学习笔记

    (如有错误,希望各位大佬指正,万分感谢!!!) 阶乘的定义是,对于非负整数 n,n 的阶乘(记作 n!)等于 n 乘以 (n - 1) 的阶乘,并且 0 的阶乘和 1 的阶乘都规定为 1。...在函数递归计算阶乘的过程中,我们定义一个函数 factorial 。...堆栈溢出是由于程序在运行时对栈空间的需求超过了其所能提供的容量,通常是由于不合理的函数调用结构、过大的局部数据或错误的代码逻辑引起的。...2.控制函数局部变量的大小 :避免在函数内部创建过大的局部数组或其他大型数据结构。如果需要较大的存储空间,可以考虑在堆上动态分配内存。 3....栈空间消耗: 每次递归调用都会在栈上分配内存来保存函数的状态和局部变量。如果递归深度过大,可能会导致栈溢出错误。 3.

    6110

    递归编程

    让我们从一个简单的例子开始,这个例子也是介绍递归的经典示例。数字N的阶乘是1和N之间所有整数的乘积,例如5的阶乘等于5 * 4 * 3 * 2 * 1= 120。...实际上是由Fact函数来计算数的阶乘的。...在Fact函数过程中,我们在N小于或等于1时结束递归调用。你的递归代码必须具有某种终止递归调用的转义逻辑,如果没有这种转义逻辑,代码将不断循环,直到 VBA 运行时因堆栈空间不足错误而中止处理。...注意,你无法使用常规错误捕获来捕获堆栈空间外错误,这被称为不可捕获的错误,将立即终止所有VBA代码的执行,且不能从无法捕获的错误中恢复。...示例:列出文件夹及子文件夹 下面的代码在工作表中列出指定文件夹中的所有子文件夹。

    80130

    【重拾C语言】十、递归程序设计

    ——递归程序设计 要计算n的阶乘(n!),可以使用递归程序设计。递归计算n的阶乘的思路如下: 基本情况:当n为0或1时,阶乘的结果为1。...递归情况:当n大于1时,n的阶乘可以表示为n乘以(n-1)的阶乘。...10.4 递归程序执行过程 递归程序的执行过程可以通过堆栈(stack)来理解。当一个函数被调用时,它的局部变量和函数调用的返回地址被压入堆栈。...如果函数内部包含递归调用,那么每次递归调用都会将新的局部变量和返回地址压入堆栈。...在递归程序执行过程中,每个递归调用都会占用一些内存空间,并且会在堆栈上创建一个新的帧(frame),包含局部变量和返回地址。

    10010

    探索c#之尾递归编译器优化

    在阶乘过程中,堆栈需要保存每次(RecFact)调用的返回地址及当时所有的局部变量状态,期间堆栈空间是无法释放的(即容易出现溢出)。 为了优化堆栈占用问题,从而提出尾递归优化的办法。.../各种状态值,而方法遗留在堆栈上的数据完全可以释放掉,这是尾递归优化的核心思想。...由于尾递归期间,堆栈是可以释放/再利用的,也就解决递归过深而引起的溢出问题,这也是尾递归的优势所在。 编译器优化 尾递归优化,看起来是蛮美好的,但在net中却有点乱糟糟的感觉。...C#/32位或C#/Debug模式中JIT是不进行优化的。 ?...如何定义复杂的尾递归呢?通常是后继传递模式(CPS)。 F#中在debug模式下,需要在编译时配置: ? 总结 在C#语言(过程式/面向对象编程思想)中,优先考虑的是循环,而不是递归/尾递归。

    1.4K70

    Java学习历程之————进阶篇(十)

    那下面我们通过一个具体的例子来讲解一下在Java中,方法是如何调用自身的。...二、递归的特性 当一个方法调用它自身的时候,堆栈就会给新的局部变量和自变量分配内存,方法代码就带着这些新的变量从头执行。...递归调用并不产生方法新的拷贝。只有参数是新的。每当递归调用返回时,旧的局部变量和自变量就从堆栈中清除,运行从方法中的调用点重新开始。递归方法可以说是像“望远镜”一样,可以自由伸缩。...这类错误在使用递归时是很常见的。 三、实战 下面我们来对比循环方法与递归方法 题目:求n!...,即求n的阶乘问题 //求一个数字的阶乘 package jinjie10; import java.util.Scanner; public class jiecheng { public

    29320

    6 个新奇的编程方式,改变你对编码的认知

    这听起来很抽象,所以我们来看看cat中的一个简单例子 : 在这里,我们将两个数字推入堆栈,然后调用该+函数,将两个数字从堆栈中弹出,并将其添加到堆栈中的结果:代码的输出为5。...foo调用的第一项在堆栈中,将它与10,并且推动任一True或 False背面压入堆栈。 接下来,我们将值0和42输入堆栈:我们将它们包括在括号中以确保它们未被执行就推入堆栈。...看起来你必须记住或想象堆栈的当前状态,而不是能够从代码中的变量名称中读取它,这可能使得很难推断代码。...如果您使用像Prolog这样的声明性语言对数字进行排序 ,则应该描述所需的输出:“我需要相同的值列表,但索引中的每个项目 i应小于或等于索引处的项目i + 1”。...例如,prolog中简单数独求解器的代码,只是列出了解决的数独谜题的每行,每列和对角线应该是什么样的: 以下是数独解算器的运行结果: 不幸的是,声明式编程语言很容易造成性能瓶颈。

    2.4K50

    递归的递归之书:引言到第四章

    在存在更简单解决方案的情况下,递归被过度使用。递归算法可能难以理解,性能较差,并容易导致堆栈溢出错误。...为c()调用创建一个新的帧对象并将其放置在调用堆栈上,其中包含c()的局部spam变量 ❻。随着这些函数的返回,帧对象从调用堆栈中弹出。程序执行知道要返回到哪里,因为返回信息存储在帧对象中。...当在源代码中使用局部变量时,将使用顶部帧对象中具有该名称的变量。 每个运行的程序都有一个调用堆栈,多线程程序每个线程都有一个调用堆栈。但是当您查看程序的源代码时,您无法在代码中看到调用堆栈。...图 1-9:调用堆栈跟踪每个函数调用中“number”局部变量的值 当基本情况达到时,代码不会立即停止,这一点对于下一章中的阶乘计算非常重要。请记住,递归情况之后的任何代码仍然必须运行。...在这个图中,堆栈中的每张卡片代表一个函数调用。每张卡片的顶部是函数名和调用时传递的参数。其下是局部变量:numbers参数,以及在调用过程中创建的head和tail局部变量。

    64210

    实用调试技巧(1)

    2.2 调试的基本步骤 发现程序错误的存在 以隔离、消除等方式对错误进行定位 确定错误产生的原因 提出纠正错误的解决办法 对程序错误予以改正,重新测试 2.3 Debug和Release的介绍 Debug...查看临时变量的值有以下三种方式: 其中,监视是最实用的,你可以在里面观察任何你想观察的东西;自动窗口会根据你调试的那一行的上下行自动给出监视的变量,不可以自己改变;局部变量只能监视你代码中创建的局部变量...3.3.3 查看调用堆栈 通过调用堆栈,可以清晰的反应函数的调用关系以及当前调用所处的位置。...i和arr是局部变量,是放在内存中栈区上的,栈区内存的使用习惯:先使用高地址处的空间,再使用低地址处的空间;又因为数组随着下标的增长,地址是由低到高变化的。...(Release版本中i的地址比arr[0]的地址要低;变量在内存中开辟的顺序发生了变化,影响到了程序执行的结果)

    14110

    Java中如何检测并处理栈溢出错误?

    在Java中,栈溢出错误(StackOverflowError)是指当方法调用堆栈的深度超过了虚拟机所允许的最大值时发生的错误。...为了检测和处理栈溢出错误,我们可以采取以下措施: 1、了解栈溢出错误的原因: 栈溢出错误通常是由于方法调用的递归深度过大而导致的。每当调用一个方法时,都会将方法的返回地址和局部变量等信息保存在栈中。...例如,对于一个计算阶乘的递归方法,正确的终止条件应该是n等于0或1。 4、优化递归算法: 如果发现递归调用深度过大,可以考虑优化递归算法。...5、异常处理: 栈溢出错误是一个严重的错误,通常无法通过捕获和处理异常来解决。因此,在代码中并没有专门的处理栈溢出错误的机制。...当栈溢出错误发生时,JVM会抛出StackOverflowError异常,并终止程序的执行。可以在日志中记录栈溢出错误的信息,以便进行排查和调试。

    27410

    【错误记录】VMware 虚拟机报错 ( 向 VMWare 虚拟机中的 Ubuntu 系统拷贝文件时磁盘空间不足 )

    文章目录 一、报错信息 二、解决方案 一、报错信息 ---- 磁盘空间不足 二、解决方案 ---- 关闭虚拟机 , 在虚拟机关闭状态下 , 显示如下界面 , 点击 " 编辑虚拟机设置 " 选项 ,...选择 " 虚拟机设置 " 对话框 硬件 中的 " 硬盘 " 选项 , 点击右侧的 " 扩展 " 按钮 ; 输入要扩展的最大磁盘大小 , 进入系统后 , 执行 df 命令 , 查看 octopus@...apt-get install gparted 命令 , 安装 gparted 磁盘分区软件 ; 执行 sudo gparted 命令 , 弹出 GParted 软件图形窗口 ; 其中显示 20GB 的空间已经分配完毕..., 130GB 的空间待分配 ; 鼠标左键点击左侧 20GB 的空间 , 然后上方的 图标就会显示高亮 , 不选中的情况下是黑色的 ; 点击右箭头按钮 , 即可分配空间 , 这里全部拉满

    1.1K10

    漫谈递归转非递归

    其中,具体要保存的内容包括:局部变量、形参、调用函数地址、返回值。那么,如果递归调用N次,就要分配N*局部变量、N*形参、N*调用函数地址、N*返回值。这势必是影响效率的。...递归就是利用系统的堆栈保存函数当中的局部变量来解决问题的,说白了就是利用堆栈上的一堆指针指向内存中的对象,并且这些对象一直不被释放,直到遇到简单情境时才一一出栈释放,所以总的开销就很大。...这种方法几乎是通用的方法,因为递归本身就是通过堆栈实现的,我们只要把递归函数调用的局部变量和相应的状态放入到一个栈结构中,在函数调用和返回时做好push和pop操作,就可以了(后面有一个模拟快排的例子)...第二种方法:借助堆栈的循环结构算法。这种方法常常适用于某些局部变量有依赖关系,且需要重复执行的场景,例如二叉树的遍历算法,就采用的这种方法。       最后,通过一个用堆栈模拟快排的例子来结束本文。...通过一个结构体record来记录函数的局部变量和相应的状态。

    1.8K70

    改变开发者编码思维的六种编程范式

    依赖类型的语言,如Idris,甚至在未来的Scala中,可能会提供更轻量级和更实用的替代方案,这仍然可以显著的提高类型系统捕捉错误的能力。...注意,在CAT中函数不指定输入参数:所有参数都是从堆栈中隐式读取的。 foo调用堆栈上弹出堆栈的第一个选项,将其与10进行比较,并将true或false返回到堆栈。...接下来,我们将0和42推到堆栈:我们把它们放在括号中以确保它们推到未被评估堆栈上。这是因为这是因为它们将被用作“then”和“else”分支(分别)用于调用下一行的 if 函数。...似乎你必须记住或想象堆栈的当前状态,而不能够从代码中的变量名读取它,这会使代码很难理解。 声明式编程(Declarative programming) ?...如果使用声明式语言如Prolog来进行数字排序,可直接描述你想要的输出:“我想要相同的值列表,但每个索引i中的每个项目都应小于或等于索引为i+ 1的项”。

    2.2K100

    递归与尾递归简析

    当递归调用是函数最后执行的一步时,该递归函数就是尾递归。 与之相对的是非尾递归函数,你先执行递归调用,然后获取递归调用的结果进行计算, 这样你需要先获取每次递归调用的结果,才能获取最后的计算结果。...看下面计算n阶乘的函数,它是一个非尾递归函数。我们发现cal(n-1)返回的值被cal(n)使用,因此对cal(n-1)的调用并不是cal(n)所做的最后一步。...,因为递归调用是最后一条statement,所以在当前函数中没有什么可做的,这样没有必要保存当前函数的堆栈结构了。...而非尾递归函数调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。 一个non-tail递归函数可以优化成尾递归函数吗?...我们还是以n阶乘为例,其方法是再使用一个参数,并在第二个参数中累积阶乘值。当n达到0时,返回累积值。

    83830

    在IntelliJ IDEA中多线程并发代码的调试方法

    即:100的阶乘 + 100000的阶乘。 数学不好的同学看这里,100 阶乘就是:1 2 3 …… 100 = ? ,简写为100!...Frames 与 Thread 面板 调试工具窗口的“Frames”面板包含一个下拉菜单。它的关注点在:由于断点而导致暂停的线程,并显示这些线程的调用堆栈信息。...在下图中,断点位于main()方法中如图所示的位置,Frame向我们显示了主线程的调用堆栈。 ? 如果要检查其他线程的调用堆栈,则可以从下拉列表中进行选择。 ?...当应用程序在该断点处暂停时,我们应该在此窗格中至少看到三个线程-“main”,“Thread 1”和“Thread 2”(请看下面的屏幕截图)。您可以双击每个线程以观察其调用堆栈。 ?...条件断点-只挂起符合条件的线程 假设我正在解决该程序中的错误,并且我只需要在“Thread 2”开始运行时就暂停执行。

    3.2K20

    关于我、重生到500年前凭借C语言改变世界科技vlog.8——函数递归

    1.递归的介绍 在 vlog.2 的 printf 函数的返回值举例中,我们使用多次递归的方式实现了同一个函数的返回值调用,但这只是一个简易的递归,不算真正意义上的递归,那么什么是递归?...当 n == 0 的时候,此时 n 的阶乘是 1 ,n > 0时阶乘可根据公式计算 那么我们可以写出阶乘函数 Fact ,Fact(n) 是求 n 的阶乘,那么Fact(n-1)就是求 n-1 的阶乘...,当函数调用完之后依次从最后一个子程序往第一个程序打印 4.递归与迭代 Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销 在C语言中每一次函数调用,都需要为本次函数调用在内存的栈区...,申请一块内存空间来保存函数调 用期间的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。...,会容易出现程序错误 5.递归经典问题的拓展 青蛙跳台阶问题 汉诺塔问题 这两个问题将在下一期vlog拓展推出,欢迎大家看我的下一期推文 希望读者们多多三连支持 小编会继续更新 你们的鼓励就是我前进的动力

    8910

    RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案

    本篇文章将全面解读这一错误的成因,并提供有效的解决方案,帮助你在开发中轻松规避递归深度问题。 1. 什么是递归?...RuntimeError: maximum recursion depth exceeded 错误剖析 2.1 错误的成因 ️ 在Python中,每个线程都有一个固定的递归深度限制,默认是1000层。...然而,对于某些需要深度递归的算法,如树遍历或图搜索,默认的递归深度可能不足,导致程序无法正常运行。...2.2 常见场景分析 以下是几个容易出现该错误的常见场景: 深度优先搜索:在遍历深度较大的树或图时,递归深度超限尤为常见。 数学递归问题:如计算斐波那契数列、阶乘等。...某些编译器或解释器可以自动优化尾递归,减少堆栈消耗。然而,遗憾的是,Python 不支持尾递归优化。

    22410

    Objective-Ckotilin 混编项目函数调用栈异常排查笔记(1) - Fast Unwind 与序章

    Backtrace(函数调用栈) 中的一个 栈帧。...对于帧 0,这是 APP 暂停或终止时在线程上执行的机器指令的地址。对于其他栈帧,这是在控制权返回到该栈帧之后执行的第一条机器指令的地址。 main:在完全符号化的崩溃报告中,代表函数的名称。...如果源文件的行号为0,则表示该 栈帧 不会映射到原始代码中的特定代码行。...分配堆栈空间、调用其他函数、保存非易失性寄存器或使用异常处理的每个函数都必须具有 prolog epilog(尾声) epilog (尾声) 是函数结束部分的指令。...epilog 的指令会将堆栈剪裁为固定分配大小(如有必要),解除分配固定堆栈分配,通过从堆栈中弹出其保存的值来还原非易失性寄存器,然后返回。

    1.5K10

    【C语言】函数递归总结

    递归举例 2.1 举例1:求n的阶乘 ⼀个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且0的阶乘为1。⾃然数n的阶乘写作n!...n的阶乘的递归公式如下: 那我们就可以写出函数Fact求n的阶乘,假设Fact(n)就是求n的阶乘,那么Fact(n-1)就是求n-1的阶乘,函数如下: int Fact(int n) { if...(int n) { if(n==0) return 1; else return n*Fact(n-1); } Fact函数是可以产生正确的结果,但是在递归函数调用的过程中涉及一些运行时的开销。...在C语言中每一次函数调用,都要需要为本次函数调用在栈区申请一块内存空间来保存函数调用期间 的各种局部变量的值,这块空间被称为运行时堆栈,或者函数栈帧。...函数不返回,函数对应的栈帧空间就⼀直占用,所以如果函数调用中存在递归调用的话,每一次递归 函数调用都会开辟属于自己的栈帧空间,直到函数递归不再继续,开始回归,才逐层释放栈帧空间。

    7310
    领券