什么是递归?什么是迭代?

本文通过一个生动的故事介绍了递归和迭代的基本概念,并对比了两者的区别。递归是一种自我调用的方法,适用于解决数据定义递归或需要回溯的问题;而迭代则是重复执行一系列指令,适用于逼近解决方案的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       许多时候,人们对事物常常不能获得绝对信号(态)地感知,那么就尝试选择能否获得变化信号(势)的意识,这些变化既包括递归也涉及迭代。那么两者究竟有何区别呢?

640?wx_fmt=png


先讲个故事吧。

从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?“从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?‘从前有座山,山里有座庙,庙里有个老和尚,正在给小和尚讲故事呢!故事是什么呢?……’”。

这个故事永远也讲不完,因为没有递归结束条件。老师讲递归时总是说,递归很简单,一个递归结束条件,一个自己调用自己。如果递归没有结束条件,那么就会无限递归下去。在编程的时候,没有递归结束条件或者递归过深,一般会造成栈溢出。

递归算法一般用于解决三类问题:

(1)数据的定义是按递归定义的。(Fibonacci函数)

(2)问题解法按递归算法实现。(回溯)

(3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)


迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。

为什么使用迭代而不用递归呢?

很明显,使用递归时每调用一次,就需要在栈上开辟一块空间,而使用迭代就不需要了,因此,很多时候设计出了递归算法,还要想法设法修改成迭代算法。


假如现在我们不考虑编程,我们仅仅看一下上面使用递归和迭代求1+2+3…+n的过程。

使用递归:

sum(5)
5+sum(4)
5+4+sum(3)
5+4+3+sum(2)
5+4+3+2+sum(1)
5+4+3+2+1+sum(0)
5+4+3+2+1+0
5+4+3+2+1
5+4+3+3
5+4+6
5+10
15

使用迭代

0+1=1
1+2=3
3+3=6
6+4=10
10+5=15

上面两个计算过程所需的步骤都是O(n)。但是两个计算过程的形状不一样。

递归过程是一个先逐步展开而后收缩的形状,在展开阶段,这一计算过程构造起一个推迟进行的操作所形成的的链条(这里是+),在收缩阶段才会实际执行这些操作。这种类型的计算过程由一个推迟执行的运算链条刻画,称为一个递归计算过程。要执行这种计算过程,就需要维护以后将要执行的操作的轨迹。在计算1+2+3+…+n时,推迟执行的加法链条的长度就是为了保存其轨迹需要保存的信息量,这个长度随着n值而线性增长,这样的过程称为线性递归过程。

迭代过程的形成没有任何增长或收缩。对于任意一个n,在计算的每一步,我们需要保存的就只有i,ret,这个过程就是一个迭代计算过程。一般来说,迭代计算过程就是那种其状态可以用固定数目的状态变量描述的结算过程。在计算1+2+…+n时,所需的计算步骤与n成正比,这种过程称为线性迭代过程。


程序调用自身的编程技巧称为递归( recursion)。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。


迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。


递归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。

举个例子吧:你要给某个小孩子买玩具。
递归:
你自己不太了解小孩子的需求,为了缩小范围,让你的儿子去给孙子挑选。儿子比你强点有限,但依然不太了解小孩子的需求。为了缩小范围,你又让你孙子去挑选。如此这般,直到找到合适的玩具。
迭代:
你挑了一件觉得不行,又挑了一件又不行。如此这般,直到找到合适的玩具。
所以一句话:递归是自己调用自己,每次旨在缩小问题规模。迭代是自己执行很多次,每次旨在更接近目标。


迭代是逐渐逼近,用新值覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。
递归是将一个问题分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈保存,问题规模较大时会占用大量内存。




<think>好的,我现在需要回答用户关于C++中尾递归其他递归类型的问题。首先,用户提到他们已经了解了一些关于尾递归的知识,但需要更具体的解释,尤其是在C++代码中的体现,以及识别其他类型的递归类型。 根据用户提供的引用内容,引用[3]提到尾递归是线性递归的一种特殊形式,递归调用是函数的最后一个操作,编译器可以优化为迭代,减少栈溢出的风险。引用[4]中的Scala例子展示了尾递归优化,而引用[5]给出了C++的尾递归示例。这些资料可以帮助我构建关于C++尾递归解释。 首先,我需要明确尾递归的定义,即在函数的最后一步进行递归调用,且没有后续操作。例如,用户提供的C++例子中,tail_my_print函数在递归调用后没有其他操作,这就是尾递归。而普通的递归函数my_print在递归调用后还有cout操作,因此不是尾递归。 接下来,关于其他递归类型,常见的包括线性递归、树形递归、互递归等。引用[3]提到尾递归属于线性递归,所以需要解释线性递归的特点。线性递归指每次递归调用只产生一个后续调用,形成线性结构。树形递归则像斐波那契数列那样,每次调用产生多个子调用,形成树状结构。互递归是函数之间相互调用的情况,例如A调用B,B再调用A。 另外,需要考虑用户提到的C++是否支持尾递归优化。虽然C++标准没有强制要求,但大多数编译器如GCCClang在优化模式下会进行尾调用优化。需要指出这一点,并建议用户检查编译器文档。 最后,生成相关问题时要围绕尾递归其他递归类型,比如优化方法、应用场景、编译器支持等。确保问题符合用户可能的需求,帮助他们深入理解。 总结步骤: 1. 定义尾递归,结合C++代码示例。 2. 解释递归优化机制C++编译器支持情况。 3. 列举其他递归类型,如线性、树形、互递归,并给出例子。 4. 生成相关问题,引导进一步学习。</think>### C++中的尾递归与其他递归类型解析 #### 一、C++中的尾递归 **尾递归**是递归函数的一种特殊形式,其核心特征是**递归调用是函数体中的最后一个操作**,且递归调用返回后无需执行其他计算。这种特性使得编译器可以将其优化为迭代形式,避免调用栈的深度增长,从而防止栈溢出[^3][^4]。 ##### 1. 尾递归示例(C++) 以下代码展示了普通递归与尾递归的区别: ```cpp // 普通递归(非尾递归) void my_print(int N) { if (N) { my_print(N - 1); // 递归调用后有其他操作(cout) cout << N << endl; } } // 尾递归优化版本 void tail_my_print(int start, int N) { if (start) { cout << N - start << endl; tail_my_print(--start, N); // 递归调用是最后一步操作 } } ``` - **关键区别**:尾递归版本`tail_my_print`的递归调用后没有其他操作,而普通递归版本`my_print`在递归后需要执行`cout`。 ##### 2. 尾递归优化机制 - **编译器优化**:在开启优化选项(如GCC的`-O2`)时,编译器会将尾递归转换为循环结构,消除栈帧的累积[^4][^5]。 - **验证方法**:对比优化前后的汇编代码或观察大输入下的栈溢出情况(例如输入`100000`时普通递归会崩溃,尾递归优化后正常运行)。 --- #### 二、其他递归类型 除了尾递归,常见的递归类型还包括: ##### 1. 线性递归 - **定义**:每次递归调用仅产生一个后续调用,形成线性调用链。 - **示例**:计算阶乘的递归实现: ```cpp int factorial(int n) { if (n == 0) return 1; return n * factorial(n - 1); // 递归后仍需执行乘法(非尾递归) } ``` ##### 2. 树形递归 - **定义**:每次递归调用产生多个子调用,形成树状结构。 - **示例**:斐波那契数列的递归实现: ```cpp int fib(int n) { if (n <= 1) return n; return fib(n-1) + fib(n-2); // 每个调用产生两个子调用 } ``` ##### 3. 互递归(间接递归) - **定义**:多个函数相互调用形成递归。 - **示例**: ```cpp void functionA(int n); void functionB(int n); void functionA(int n) { if (n > 0) functionB(n - 1); // A调用B } void functionB(int n) { if (n > 0) functionA(n - 1); // B调用A } ``` --- #### 三、C++对尾递归优化的支持 - **编译器依赖**:C++标准未强制要求尾递归优化,但主流编译器(如GCC、Clang)在优化模式下通常支持[^5]。 - **验证方法**:通过编译选项`-O2`启用优化,观察栈使用情况。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值