首页 > 其他分享 >递归函数(详细讲解版)

递归函数(详细讲解版)

时间:2024-11-20 20:17:51浏览次数:3  
标签:遍历 return 递归 递归函数 调用 fibonacci 详细 讲解 root

递归函数就是在函数的定义中使用函数自身的方法。这种函数调用自身的方式可以将一个复杂的问题逐步简化为相同类型的较简单问题。
 




关键要素
 
1. 终止条件
 
这是递归函数中最重要的部分。如果没有终止条件,函数会一直调用自身,导致栈溢出(程序运行时栈空间耗尽)。终止条件用于确定何时停止递归调用,返回一个明确的值。
 
- 例如,在计算斐波那契数列的函数中:
 
c   
int fibonacci(int n) {
    if (n == 0 || n == 1) {  // 终止条件,当n为0或1时,返回n
        return n;
    } else {
        return fibonacci(n - 1) + fibonacci(n - 2);
    }
}
 
 
在这个函数里,当 n 等于0或者1时,函数就不再递归调用,而是直接返回 n 的值,这就是终止条件。
 
2. 递归调用
 
在满足非终止条件的情况下,函数会调用自身,并且每次调用时,问题的规模应该逐渐减小,使得最终能够达到终止条件。
 
- 还是以斐波那契数列为例, return fibonacci(n - 1) + fibonacci(n - 2); 这部分就是递归调用。它表示斐波那契数列的第 n 项等于第 n - 1 项和第 n - 2 项之和。每次递归调用都会使 n 的值减小,直到 n 等于0或者1,达到终止条件。
 
工作原理
 
递归函数的执行过程可以用栈来理解。每当一个函数被调用时,系统会为这个函数分配一块栈空间,用于存储函数的参数、局部变量和返回地址等信息。
 
- 当一个递归函数调用自身时,新的函数调用会在栈顶分配新的空间,保存新的参数和局部变量等。
- 以 fibonacci(4) 为例,计算过程如下:
- 首先调用 fibonacci(4) ,在栈中为这个函数调用分配空间。
- 由于 n 不等于0或1,所以执行 return fibonacci(3) + fibonacci(2); 。此时,先计算 fibonacci(3) ,系统会在栈顶为 fibonacci(3) 的调用分配新空间。
- 对于 fibonacci(3) ,又会执行 return fibonacci(2) + fibonacci(1); ,继续在栈顶为新的 fibonacci(2) 调用分配空间。
- 这个过程会一直持续,直到达到终止条件 n = 0 或 n = 1 。当达到终止条件后,函数开始返回,从栈顶的函数开始,依次计算并释放栈空间,最终得到 fibonacci(4) 的结果。
 
应用场景
 
1. 数学计算
 
- 阶乘计算:除了前面提到的阶乘函数 factorial ,还有一些其他的数学公式也可以用递归实现。例如组合数的计算,组合数 C(n, k) (表示从 n 个不同元素中取出 k 个元素的组合数)可以用以下递归公式表示: C(n, k) = C(n - 1, k - 1) + C(n - 1, k) ,同时 C(n, 0) = C(n, n) = 1 作为终止条件。
 
c   
int combination(int n, int k) {
    if (k == 0 || k == n) {
        return 1;
    } else {
        return combination(n - 1, k - 1) + combination(n - 1, k);
    }
}
 
 
- 幂运算:计算一个数 x 的 n 次幂。可以用递归方式实现,当 n 为偶数时, x^n = (x^(n/2))^2 ;当 n 为奇数时, x^n = x * (x^(n - 1)) ,终止条件是当 n = 0 时,返回1。
 
c   
double power(double x, int n) {
    if (n == 0) {
        return 1;
    } else if (n % 2 == 0) {
        double temp = power(x, n / 2);
        return temp * temp;
    } else {
        return x * power(x, n - 1);
    }
}
 
 
2. 数据结构遍历
 
- 二叉树遍历:二叉树是一种常见的数据结构,它有根节点、左子树和右子树。
- 前序遍历:先访问根节点,然后递归地遍历左子树,再递归地遍历右子树。
 
c   
// 二叉树节点结构体定义
typedef struct TreeNode {
    int val;
    struct TreeNode* left;
    struct TreeNode* right;
} TreeNode;

void preorderTraversal(TreeNode* root) {
    if (root!= NULL) {
        printf("%d ", root->val);
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}
 
 
- 中序遍历:先递归地遍历左子树,然后访问根节点,再递归地遍历右子树。
 
c   
void inorderTraversal(TreeNode* root) {
    if (root!= NULL) {
        inorderTraversal(root->left);
        printf("%d ", root->val);
        inorderTraversal(root->right);
    }
}
 
 
- 后序遍历:先递归地遍历左子树,然后递归地遍历右子树,最后访问根节点。
 
c   
void postorderTraversal(TreeNode* root) {
    if (root!= NULL) {
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        printf("%d ", root->val);
    }
}
 
 
3. 分治法问题
 
- 汉诺塔问题:有三根柱子A、B、C。开始时,在A柱子上有 n 个圆盘,这些圆盘大小不同,且按照从下到上圆盘大小递减的顺序排列。目标是将所有圆盘从A柱子移动到C柱子,在移动过程中可以借助B柱子,并且要求每次只能移动一个圆盘,且大盘不能放在小盘上面。
 
c   
// 将n个圆盘从from柱,借助aux柱,移动到to柱
void hanoi(int n, char from, char aux, char to) {
    if (n == 1) {
        printf("将圆盘1从%c柱移动到%c柱\n", from, to);
    } else {
        hanoi(n - 1, from, to, aux);
        printf("将圆盘%d从%c柱移动到%c柱\n", n, from, to);
        hanoi(n - 1, aux, from, to);
    }
}
在个函数中,通过不断地将问题分解为规模更小的子问题(每次减少一个圆盘的移动问题),并利用递归解决这些子问题,最终实现了整个汉诺塔问题的求解。

标签:遍历,return,递归,递归函数,调用,fibonacci,详细,讲解,root
From: https://blog.csdn.net/2401_88186301/article/details/143760969

相关文章

  • JDBC讲解(第三篇)
    PreparedStatement接口防止SQL注入使用PreparedStatement接口是防止SQL注入的一种有效方法。SQL注入SQL注入是一种常见的网络攻击手段,攻击者通过在应用程序的输入字段中插入恶意的SQL代码,试图干扰或破坏正常的数据库操作。为了防止这种攻击,Java的JDBCAPI提供了PreparedStat......
  • 网络安全(超级详细)零基础带你一步一步走进缓冲区溢出漏洞和shellcode编写!
    零基础带你走进缓冲区溢出,编写shellcode。写在前面的话:本人是以一个零基础者角度来带着大家去理解缓冲区溢出漏洞,当然如果你是开发者更好。#注:如果有转载请注明出处!创作不易、谢谢合作。#0、环境搭建:#本次实验所用到的工具有:x32dbg:一个基于qt开发的、开源调试器。ghid......
  • 【K8S系列】imagePullSecrets配置正确,但docker pull仍然失败,进一步排查详细步骤
    如果imagePullSecrets配置正确,但在执行dockerpull命令时仍然失败,可能存在以下几种原因。以下是详细的排查步骤和解决方案。1.检查Docker登录凭证确保你使用的是与imagePullSecrets中相同的凭证进行Docker登录:1.1直接登录在命令行中,执行以下命令:docker......
  • 【Java系列】Spring Boot 配置Spring Native 详细步骤
    配置SpringNative以减少SpringBoot应用的启动时间,涉及几个关键步骤,包括设置相应的依赖、配置文件以及构建过程。以下是详细的步骤和配置示例:一、前提条件确保你的项目使用的是SpringBoot2.5或更高版本,并且使用Java11或更高版本。二、添加依赖在你的pom.x......
  • SQLServer数据库里的递归CTE详细说明
     SQLServer数据库里的递归CTE详细说明  用实例来说明:样例: --解释CTE递归的运算逻辑(代码不一定可用,但逻辑准确)WITHBOM_CTEAS(--基础层(B段):选择特定BOM物料编码的所有BOM条目,并设置层级为1SELECTBOMNOAS'TopBOM',COMPID,REQQTY,1AS......
  • CSS入门(主要讲解字体,链接,列表,表格)
    一.CSS字体1.CSS字体属性要定义字体的加粗,大小,文字样式2.设置字体系列font-family属性设置文本的字体系列。font-family属性应该设置几个字体名称作为一种"后备"机制,如果浏览器不支持第一种字体,他将尝试下一种字体。注意:如果字体系列的名称超过一个字,它必须用引号,如Fo......
  • 【网络安全】1+X应急响应(初级)最详细,最全,看完包你通过
    +X应急响应(重点)应急响应准备⼯作:应急响应概念:⽹络安全应急响应概念:应急响应体系:应急响应过程:应急准备阶段主要⼯作:⻛险评估与改进:应急响应预案制定流程:应急响应流程:应急响应保障措施:Windows系统排查:Linux系统排查:系统备份:备份概述:备份种类:系统加固:数据库加固......
  • Linux – menuconfig讲解
     原文:https://blog.csdn.net/qq_42837317/article/details/139754748menuconfig1.简介        menuconfig是一套图像化配置工具,由ncurses库提供软件支持。ncurses库提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。        menuconfig本......
  • 《刚刚问世》系列初窥篇-Java+Playwright自动化测试-5-创建首个自动化脚本(详细教程)
    1.简介前面几篇宏哥介绍了两种(java和maven)环境搭建和浏览器的启动方法,这篇文章宏哥将要介绍第一个自动化测试脚本。前边环境都搭建成功了,浏览器也驱动成功了,那么我们不着急学习其他内容,首先宏哥搭建好的环境中创建首个完整的自动化测试脚本,让小伙伴或者童鞋们提前感受感受,也是为......
  • 基于Java+SpringBoot+Vue+HTML5外卖点餐系统(源码+LW+调试文档+讲解等)/外卖点餐/点餐
    博主介绍......