关于递归思想

https://blog.youkuaiyun.com/wangxiaobupt/article/details/44198949

概念

递归就是某个函数直接或间接调用自身的问题求解过程。

通过将自身问题划分成相同性质的子问题的求解过程,这些小问题的求解过程较容易,小问题的解就构成了原问题的解。

递归算法就是一个函数通过不断对自己的调用而求得最终结果的一种思维巧妙但是开销很大的算法。

步骤

1.理解题意,待求问题的解F(X)

2.通过寻找函数G(),使得F(X)=G(F(X-1)) (原解可以用一些子问题的解来表示,寻找递推关系式)

3.且已知F(0)的值,就可以通过F(0)和G()来求F(X)的解

要点

1.如何将原问题划分成子问题

2.递归终止的条件,最小子问题的求解,允许有多个出口

3.界函数,它保证递归的规模向出口靠拢

关键

1.找出递推关系式

2.找到递归终止条件

注:深层次的递归可能导致栈溢出,可以考虑使用全局数组或动态分配数组

经典的递归问题

  • 汉诺塔问题
  • 斐波那契数列问题
  • 阶乘问题

阶乘例子:
递推关系式 f(x)=x*f(x-1)
终止条件 乘到1后,x-1=0,结束递归返回1

#include <iostream>
using namespace std;
int f(int x)
{
    if (x==0)
        return 1;
    else
        return x*f(x-1);
}
int main()
{
    int n;
    cin>>n;
    int count=f(n);
    cout<<count<<endl;
}

https://zhidao.baidu.com/question/394860461.html?qbl=relate_question_1&word=%B5%DD%B9%E9%B5%C4%CB%BC%CF%EB%BA%CD%CC%F5%BC%FE

汉诺塔问题:

首先:对于递归这一类函数,你不要纠结于他是干什么的,只要知道他的一个模糊功能是什么就行,等于把他想象成一个能实现某项功能的黑盒子,而不去管它的内部操作先,好,我们来看下汉诺塔是怎么样解决的
首先按我上面说的把递归函数想象成某个功能的黑盒子,void hanoi(int n,char one,char two,char three); 这个递归函数的功能是:能将n个由小到大放置的小长方形从one 位置,经过two位置 移动到three位置。那么你的主程序要解决的问题是要将m个的”汉诺块”由A借助B移动到C,根据我们上面说的汉诺塔的功能,我相信傻子也知道在主函数中写道:hanoi(m,A,B,C)就能实现将m个块由A借助B码放到C,对吧?所以,mian函数里面有hanoi(m,’A’,’C’,’B’);这个调用。
接下来我们看看要实现hannoi的这个功能,hannoi函数应该干些什么?
在hannoi函数里有这么三行

hanoi(n-1,one,three,two);
move(one,three);
hanoi(n-1,two,one,three);

同样以黑盒子的思想看待他,要想把n个块由A经过B搬到C去,是不是可以分为上面三步呢?
这三部是:第一步将除了最后最长的那一块以外的n-1块由one位置经由three搬到two 也就是从A由C搬到B 然后把最下面最长那一块用move函数把他从A直接搬到C 完事后 第三步再次将刚刚的n-1块借助hannoi函数的功能从B由A搬回到C 这样的三步实习了n块由A经过B到C这样一个功能,同样你不用纠结于hanoi函数到底如何实现这个功能的,只要知道他有这么一个神奇的功能就行

最后:递归都有收尾的时候对吧,收尾就是当只有一块的时候汉诺塔怎么个玩法呢?很简单吧,直接把那一块有Amove到C我们就完成了,所以hanoni这个函数最后还要加上 if(n==1)move(one,three);(当只有一块时,直接有Amove到C位置就行)这么一个条件就能实现hanoin函数n>=1时将n个块由A经由B搬到C的完整功能了。
递归这个复杂的思想就是这样简单解决的,呵呵 不知道你看懂没?纯手打,希望能帮你理解递归

总结起来就是不要管递归的具体实现细节步骤,只要知道他的功能是什么,然后利用他自己的功能通过调用他自己去解决自己的功能,最后加上一个极限情况的条件即可,比如上面说的1个的情况。

    //汉诺塔问题
    public static void hanoi(int n){
        if(n>0){
            func(n,"left","mid","right");
        }
    }

    private static void func(int n, String from, String mid,String to) {
        if(n==1){
            System.out.println("move from "+from+" to "+to);
        }else{
            func(n-1,from,to,mid);//第一步:1-i从left移动到mid
            func(1,from,mid,to);//第二步:i从left移到right
            func(n-1,mid,from,to);//第三步:1-i从mid移到right
        }
    }
<think>首先,用户的问题是:“我想了解递归思想是什么 请问递归思想详细解释”。这是一个关于递归思想的详细解释请求。 我需要参考系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法必须正确。 - 使用中文回答。 - 生成相关问题。 - 引用的段落末尾自然地添加引用标识。 - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户提供了几个引用: - [^1]:递归思想如何理解?解释了递归对新手抽象,但能解决复杂问题。 - [^2]:dfs的思想递归实现dfs,递归与dfs相似,递归进入容易理解,回溯难理解,并给出了一个C++递归程序示例。 - [^3]:递归思想,总结递归的学习心得,语言是工具,思想重要。 - [^4]:C++递归思想,强调递归需要基准条件,否则无限递归。 这些引用不是用户真正的需求,但提供了背景。用户上一次的问题是关于递归思想的解释。 我的回答结构应该清晰: 1. **介绍递归思想**:定义和基本概念。 2. **关键元素**:递归的两个核心部分:基准条件(递归终止条件)和递归步骤(调用自身)。 3. **示例**:提供简单易懂的例子,如阶乘或斐波那契数列。 4. **解释递归过程**:包括进入和回溯,使用引用中的例子。 5. **优点和缺点**:递归的便利和潜在问题(如栈溢出)。 6. **应用场景**:如DFS、排序算法等。 7. **生成相关问题**:在结尾添加相关问题。 使用中文回答。 在回答中,如果需要引用引用中的内容,末尾添加引用标识,如[^1]。 生成相关问题:在“§§相关问题§§”部分列出2-3个相关问题。 确保LaTeX语法正确:行内数学表达式用$...$,独立公式用$$...$$。 例如: - 阶乘:行内 $n! = n \times (n-1)!$ for $n > 0$,基准条件 $0! = 1$。 - 斐波那契:$$F(n) = F(n-1) + F(n-2)$$ with base cases $F(0) = 0$, $F(1) = 1$. 参考引用: - 从[^1]:递归抽象但强大。 - 从:递归有进入和回溯,示例程序。 - 从[^3]:递归思想通用,语言无关。 - 从[^4]:基准条件重要性,防止无限递归。 构建回答: 1. **开头**:定义递归思想。 2. **核心部分**: - 基准条件(递归终止条件)。 - 递归步骤。 3. **示例**:阶乘或斐波那契。 4. **过程解释**:使用中的例子解释进入和回溯。 5. **优点和注意事项**:简洁但可能效率低。 6. **应用**:DFS、算法等。 7. **结尾**:生成相关问题。 相关问题: 1. 递归在深度优先搜索(DFS)中是如何应用的? 2. 递归有哪些常见问题?如何避免无限递归? 3. 递归和迭代有什么区别?各有什么优缺点? </think> ### 递归思想的详细解释 递归思想是一种编程和数学中常用的解决问题的方法,其核心在于**函数直接或间接地调用自身**,通过不断分解问题为更小的同类子问题,直到达到一个简单到可以直接解决的“基准条件”(递归终止条件),然后逐步回溯组合结果。递归抽象但强大,特别适合处理具有自相似结构的问题,如树遍历、分治算法等[^1]。下面我将逐步解释递归思想的关键要点,确保内容清晰易懂。 #### 1. **递归的核心组成** 递归函数必须包含两个基本要素: - **基准条件(Base Case)**:这是递归的终止点。当问题规模减小到一定程度时,不再递归调用,而是直接返回结果。如果缺少基准条件,递归会无限循环,导致栈溢出或程序崩溃[^4]。例如,计算阶乘 $n!$ 时,基准条件是 $n = 0$ 时 $0! = 1$。 - **递归步骤(Recursive Step)**:函数调用自身处理更小的子问题,并将结果组合起来解决原问题。例如,阶乘的递归定义为 $n! = n \times (n-1)!$ for $n > 0$。 #### 2. **递归的工作原理:进入与回溯** 递归过程分为“进入”(递归调用)和“回溯”(返回结果)两个阶段: - **进入阶段**:函数逐层调用自身,问题规模逐步减小。例如,引用[^2]中的C++程序示例:输入 $n$,函数 `func(u)` 在 `u > 0` 时先输出进入信息再调用 `func(u-1)`。 - **回溯阶段**:当达到基准条件后,函数开始逐层返回结果,每层回溯时处理剩余逻辑。在 `func(u)` 中,当 `u == 0` 时返回,然后输出回溯信息,显示从深层向浅层返回的过程。 示例代码简化如下(引用自): ```cpp void func(int u) { if (u == 0) return; // 基准条件 cout << "进入深度: " << u << endl; func(u-1); // 递归调用 cout << "回溯深度: " << u << endl; } ``` 如果输入 `n=2`,输出序列为: ``` 进入深度: 2 进入深度: 1 回溯深度: 1 回溯深度: 2 ``` 这展示了递归的“层层深入,直到触底,再步步回溯”的特性[^3]。回溯由系统自动执行,是递归的难点,但通过调试输出可以可视化这个过程。 #### 3. **简单示例:阶乘计算** 阶乘是一个经典递归问题。定义递归函数: - 基准条件:$n = 0$ 时,$0! = 1$。 - 递归步骤:$n > 0$ 时,$n! = n \times (n-1)!$。 用LaTeX表示独立公式: $$ \text{factorial}(n) = \begin{cases} 1 & \text{if } n = 0 \\ n \times \text{factorial}(n-1) & \text{if } n > 0 \end{cases} $$ Python实现: ```python def factorial(n): if n == 0: # 基准条件 return 1 return n * factorial(n-1) # 递归步骤 # 示例:factorial(3) = 3 * factorial(2) = 3 * (2 * factorial(1)) = 3 * (2 * (1 * factorial(0))) = 3 * 2 * 1 * 1 = 6 ``` 计算 $n=3$ 时: - 进入阶段:调用 `factorial(3) → factorial(2) → factorial(1) → factorial(0)`。 - 回溯阶段:`factorial(0)` 返回1 → `factorial(1)` 返回1*1=1 → `factorial(2)` 返回2*1=2 → `factorial(3)` 返回3*2=6。 #### 4. **递归的优缺点** - **优点**: - 代码简洁直观,能优雅地解决复杂问题(如树遍历)。 - 思想通用,语言无关(C、Java、Python等均可实现)[^3]。 - **缺点**: - 理解抽象:回溯过程隐藏,新手易困惑。 - 效率问题:重复计算可能导致性能低下(如斐波那契数列的递归实现)。 - 栈溢出风险:递归深度过大时,可能耗尽系统栈空间。 #### 5. **常见应用场景** 递归广泛用于算法领域: - **深度优先搜索(DFS)**:递归自然实现树的遍历或图的搜索。 - **分治算法**:如快速排序、归并排序,将问题拆分成子问题解决。 - **数学问题**:斐波那契数列($F(n) = F(n-1) + F(n-2)$,基准条件 $F(0) = 0, F(1) = 1$)、汉诺塔等。 #### 6. **学习建议** 理解递归的关键是: - **从小问题入手**:先用简单例子(如阶乘)练习。 - **可视化过程**:通过打印输出(如引用[^2]的方法)观察进入和回溯。 - **确保基准条件**:避免无限递归[^4]。 递归思想是算法基础,掌握后能提升问题解决能力。记住:语言只是工具,核心是分解问题的思想
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值