精华内容
下载资源
问答
  • 关于递归算法不正确的是
    2021-05-24 08:46:19

    c语言递归算法如何实现

    发布时间:2020-09-22 14:22:05

    来源:亿速云

    阅读:186

    作者:小新

    这篇文章将为大家详细讲解有关c语言递归算法如何实现,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

    递归就是一个方法自己调用自己。在编程语言中,如果一个程序允许您在同一个函数中调用一个函数,那么它就被称为函数的递归调用。void recursion() {

    recursion(); /* 函数调用本身 */

    }

    int main() {

    recursion();

    }

    C语言支持递归,即一个调用自身的函数。但是在使用递归时,程序员需要小心定义函数的退出条件,否则它将进入无限循环。

    递归函数对于解决许多数学问题非常有用,例如计算一个数的阶乘、生成斐波那契级数等。

    数的阶乘

    下面的例子使用递归计算一个给定的数的阶乘函数#include

    unsigned long long int factorial(unsigned int i) {

    if(i <= 1) {

    return 1;

    }

    return i * factorial(i - 1);

    }

    int main() {

    int i = 12;

    printf("Factorial of %d is %d\n", i, factorial(i));

    return 0;

    }

    输出:Factorial of 12 is 479001600

    斐波那契系列

    以下示例使用递归函数为给定数字生成斐波那契(Fibonacci)系列#include int fibonacci(int i) {

    if(i == 0) {

    return 0;

    }

    if(i == 1) {

    return 1;

    }

    return fibonacci(i-1) + fibonacci(i-2);}int main() {

    int i;

    for (i = 0; i < 10; i++) {

    printf("%d\t\n", fibonacci(i));

    }

    return 0;}

    输出:0

    1

    1

    2

    3

    5

    8

    13

    21

    34

    关于c语言递归算法如何实现就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

    更多相关内容
  • 递归算法的优缺点.pdf

    2020-07-16 06:44:45
    实用标准文案 递归算法的优缺点 1 优点 结构清晰可读性强而且容易用数学归纳法来证明算法的正确性 因此它为设计 算法调试程序带来很大方便 2 缺点 递归算法的运行效率较低 无论是耗费的计算时间还是占用的存储空间都...
  • 递归函数具有很好的可读性和可维护性,但是大部分情况下程序效率不如非递归函数,所以在程序设计中一般喜欢先用递归解决问题,在保证方法正确的前提下再转换为非递归函数以提高效率。 函数调用时,需要在栈中分配新...

    递归函数具有很好的可读性和可维护性,但是大部分情况下程序效率不如非递归函数,所以在程序设计中一般喜欢先用递归解决问题,在保证方法正确的前提下再转换为非递归函数以提高效率。

    函数调用时,需要在栈中分配新的帧,将返回地址,调用参数和局部变量入栈。所以递归调用越深,占用的栈空间越多。如果层数过深,肯定会导致栈溢出,这也是消除递归的必要性之一。递归函数又可以分为尾递归和非尾递归函数,前者往往具有很好的优化效率,下面我们分别加以讨论。

    尾递归函数

    尾递归函数是指函数的最后一个动作是调用函数本身的递归函数,是递归的一种特殊情形。尾递归具有两个主要的特征:

      1. 调用自身函数(Self-called);

      2. 计算仅占用常量栈空间(Stack Space)。

    为什么尾递归可以做到常量栈空间,我们用著名的fibonacci数列作为例子来说明。

    fibonacci数列实现方法一般是这样的:

    int FibonacciRecur(int n) {
      if (0==n) return 0;
      if (1==n) return 1;
      return FibonacciRecur(n-1)+FibonacciRecur(n-2);
    }

    不过需要注意的是这种实现方法并不是尾递归,因为尾递归的最后一个动作必须是调用自身,这里最后的动作是加法运算,所以我们要修改一下:

    int FibonacciTailRecur(int n, int acc1, int acc2) {
      if (0==n) return acc1;
      return FibonacciTailRecur(n-1, acc2, acc1+acc2);
    }

    好了,现在符合尾递归的定义了,用gcc分别加-O和-O2选项编译,下面是部分汇编代码,

    -O2汇编代码

    FibonacciTailRecur:
    .LFB12:
            testl   %edi, %edi
            movl    %esi, %eax
            movl    %edx, %esi
            je      .L4
            .p2align 4,,7
    .L7:
            leal    (%rax,%rsi), %edx
            decl    %edi
            movl    %esi, %eax
            testl   %edi, %edi
            movl    %edx, %esi
            jne     .L7   // use jne 
    .L4:
            rep ; ret

    -O汇编代码

    FibonacciTailRecur:
    .LFB2:
            pushq   %rbp
    .LCFI0:
            movq    %rsp, %rbp
    .LCFI1:
            subq    $16, %rsp
    .LCFI2:
            movl    %edi, -4(%rbp)
            movl    %esi, -8(%rbp)
            movl    %edx, -12(%rbp)
            cmpl    $0, -4(%rbp)
            jne     .L2
            movl    -8(%rbp), %eax
            movl    %eax, -16(%rbp)
            jmp     .L1
    .L2:
            movl    -12(%rbp), %eax
            movl    -8(%rbp), %edx
            addl    %eax, %edx
            movl    -12(%rbp), %esi
            movl    -4(%rbp), %edi
            decl    %edi
            call     FibonacciTailRecur  //use call
            movl    %eax, -16(%rbp)
    .L1:
            movl    -16(%rbp), %eax
            leave
            ret

    可以看到-O2时用了jne命令,每次调用下层递归并没有申请新的栈空间,而是更新当前帧的局部数据,重复使用当前帧,所以不管有多少层尾递归调用都不会栈溢出,这也是使用尾递归的意义所在。

    而-O使用的是call命令,这会申请新的栈空间,也就是说gcc默认状态下并没有优化尾递归,这么做的一个主要原因是有时候我们需要保留帧信息用于调试,而加-O2优化后,不管多少层尾递归调用,使用的都是第一层帧,是得不到当前帧的信息的,大家可以用gdb调试下就知道了。

    除了尾递归,Fibonacci数列很容易推导出循环实现方式:

    int fibonacciNonRecur(int n) {
      int acc1 = 0, acc2 = 1;
      for(int i=0; i<n; i++){
        int t = acc1;
        acc1 = acc2;
        acc2 += t;
      }
      return acc1;
    }

     在我的机器上,全部加-O2选项优化编译,运行时间如下(单位微秒):

    n

    fibonacciNonRecur

    FibonacciTailRecur

    FibonacciRecur

    20

    1

    1

    123

    30

    1

    1

    14144

    将fibonacci函数的迭代,尾递归和递归函数性能比较,可以发现迭代和尾递归时间几乎一致,n的大小对迭代和尾递归运行时间影响很小,因为只是多执行O(n)条机器指令而已。但是n对递归函数影响非常大,这是由于递归需要频繁分配回收栈空间所致。正是由于尾递归的高效率,在一些语言如lua中就明确建议使用尾递归(参照《lua程序设计第二版》第6章)。

    非尾递归函数

    编译器无法自动优化一般的递归函数,不过通过模拟递归函数的过程,我们可以借助于栈将任何递归函数转换为迭代函数。直观点,递归的过程其实是编译器帮我们处理了压栈和出栈的操作,转换为迭代函数就需要手动地处理压栈和出栈。

    下面我们以经典的快速排序为例子。

    int partition(int *array, int low, int high) {
        int val = array[low];
        while(low < high) {
          while(low<high && array[high]>=val) --high;
         swap(&array[low], &array[high]);
         while(low<high && array[low]<=val) ++low;
         swap(&array[low], &array[high]);
       }
       return low;
     }
      void Quicksort(int *array, int b, int e) {
        if (b >= e) return;
       int p = partition(array, b, e);
        Quicksort(array, b, p-1);
        Quicksort(array, p+1, e);
     }

     

    其实不难看出快速排序的递归算法就是一个二叉树的先序遍历过程,先处理当前根节点,然后依次处理左子树和右子树。将快速排序递归算法转换为非递归相当于将二叉树先序遍历递归算法转为非递归算法。

    二叉树先序遍历递归算法伪码:

    void PreorderRecursive(Bitree root){
      if (root) {
        visit(root);
        PreorderRecursive(root->lchild); 
        PreorderRecursive(root->rchild); 
      }
    }

    二叉树先序遍历非递归伪码:

    void PreorderNonRecursive(Bitree root){
      stack stk;
      stk.push(root);
      while(!stk.empty()){
        p = stk.top();
        visit(p);
        stk.pop();
        if(p.rchild) stk.push(stk.rchild);
        if(p.lchild) stk.push(stk.lchild);
      }
    }

    每次处理完当前节点后将右子树和左子树分别入栈,类似地,我们也很容易得到快速排序的非递归算法实现。partition将数组分为左右两部分,相当与处理当前节点,接下来要做的就是将左右子树入栈,那么左右子树需要保存什么信息呢?这个是处理非递归函数的关键,因为被调用函数信息需要压入栈中。快速排序只需要保存子数组的边界即可。

    void QuicksortNonRecur(int *array, int b, int e) {
      if (b >= e) return;
      std::stack< std::pair<int, int> > stk;
      stk.push(std::make_pair(b, e));
      while(!stk.empty()) {
        std::pair<int, int> pair = stk.top();
        stk.pop();
        if(pair.first >= pair.second) continue;
        int p = partition(array, pair.first, pair.second);
        if(p < pair.second) stk.push(std::make_pair(p+1, e));
        if(p > pair.first) stk.push(std::make_pair(b, p-1));
      }
    }

    总结

    虽然将递归函数转换为迭代函数可以提高程序效率,但是转换后的迭代函数往往可读性差,难以理解,不易维护。所以只有在特殊情况下,比如对栈空间有严格要求的嵌入式系统,才需要转换递归函数。大部分情况下,递归并不会成为系统的性能瓶颈,一个代码简单易读的递归函数常常比迭代函数更易维护。

    展开全文
  • 算法设计与分析之递归算法

    千次阅读 2020-11-26 22:40:27
    简单易懂的介绍了递归算法的原理和设计方法


    前言

            递归算法真的是一座大山,想跨过它需要付出许多努力,这个过程中你可能想着放弃,当你有这个念头的时候,请咬牙坚持下去,因为当你翻越它之后将会欣赏到另一番风景。

    在这里插入图片描述

    一、递归算法概念

            直接或间接地调用自身的算法称为递归算法。用函数自身给出定义的函数称为递归函数。使用递归技术往往会使代码更简洁,使算法的描述更清晰且容易理解。

    二、递归算法的设计步骤

    1、分析并划分问题
            分析问题,将问题分解成若干个规模较小相互独立,但类型相同的子问题。需要注意的是各子问题的解必须能够组合成原始问题的一个完整答案。
    2、设计递归函数
            根据步骤1问题分解的过程,设计出相应的递归函数。设计递归函数时需要注意它的两要素:边界条件和递归方程,递归函数只有具备了这两个要素,才能在有限次计算后得出结果。
    3、设计程序
            根据递归函数设计出解决问题的程序。

    三、示例

            以下只给出题目,需要具体解析请查看文章递归整理及几个经典题目

    1、求阶乘

            给定一个非负整数n,求它的阶乘n!。

    2、斐波那契数列

            斐波那契数列的排列是:0,1,1,2,3,5,8,13,21,34,55,89,144……依次类推下去,你会发现,它后一个数等于前面两个数的和。在这个数列中的数字,就被称为斐波那契数。

    3、汉诺塔

            有三根杆子X,Y,Z。X杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至Y杆:
            1、每次只能移动一个圆盘;
            2、大盘不能叠在小盘上面。

    4、排列问题

            输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。

    四、递归算法优缺点分析

    1、优点

            结构清晰,可读性强,而且容易用数学归纳法来证明算法的正确性,因此它为设计算法、调试程序带来很大方便。

    2、缺点

            递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。

    解决方法
            在递归算法中消除递归调用,使其转化为非递归算法。
            1、采用一个用户定义的栈来模拟系统的递归调用工作栈。该方法通用性强,但本质上还是递归,只不过人工做了本来由编译器做的事情,优化效果不明显。
            2、用递推来实现递归函数。
            3、通过变换能将一些递归转化为尾递归, 从而迭代求出结果。后两种方法在时空复杂度上均有较大改善,但其适用范围有限。

    小结

            对递归算法的介绍就到这里啦,希望这篇文章能给予你一些帮助,感谢各位人才的:点赞、收藏和评论,我们下次见。

    在这里插入图片描述

    展开全文
  • Python递归算法详解

    千次阅读 2022-02-23 17:17:37
    递归的概念很简单,如果函数包含了对其自身的调用,该函数就是递归的。 递归(Recursion),在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。 在使用递归时,需要注意以下几点: 递归就是在过程或...
  • 插入排序递归算法与非递归算法比较实验 实验题目 插入排序递归算法与非递归算法比较实验 实验要求 画出运行时间与n变化曲线对比图,并分析原因 实验目的 1、 掌握递归算法的思想 2、 编写实现插入排序的递归与非递归...
  • 递归算法和过程的详解

    千次阅读 2021-02-07 08:37:00
    递归算法包含的两个部分 1 由其自身定义的与原始问题类似的更小规模的子问题(只有数据规模不同),它使递归过程持续进行,称为一般条件。 2 所描述问题的最简单的情况,它是一个能控制递归过程结束的条件,称为...
  • js递归算法

    2020-12-05 16:54:24
    js递归算法 递归作为算法的基础之一,如果需要学习算法,递归是避开的,今天就带大家理解递归的原理以及作用。 使用递归可以压缩代码量以及增加代码的可读性 提示:以下是本篇文章正文内容,下面案例可供参考 ...
  • 递归算法及经典例题详解

    千次阅读 2021-10-02 14:51:27
    而在面对递归问题的求解时,我们还需要考虑第四点:**当满足终止条件时,要如何缩小函数值并让其进入下一层循环中**
  • 递归算法分析-最简单的例子

    万次阅读 2021-08-20 14:40:38
    递归算法在算法设计中,是一种比较难以理解的算法存在,感觉递归算法无限套娃,最终居然也能返回正确结果,对于初入坑的童靴是一个神一般的存在。本文通过对递归算法的一个例子,从最简单的例子中,逐步撕开其神秘...
  • 递归算法总结

    千次阅读 2019-04-30 14:24:34
    1 递归算法初探 本段内容大部分摘自《linux C一站式编程》,作者是宋劲松老师,我认为这是目前看到的国内关于linux C编程的最好的一本技术书籍,强烈推荐! 关于递归的一个简单例子是求整数阶乘,n!=n*(n-1)!,0!=...
  • C语言二叉树遍历前序非递归算法,简单易懂,正确无误
  • 递归算法及经典递归实现

    千次阅读 2019-10-23 16:04:44
    递归 递归就是方法自己调用自己,每次调用时传入不同的变量,递归有助于编程者解决复杂的问题,同时可以让代码变得简洁。...使用 递归算法的 前提有两个: (1) 原问题可以层层分解为类似的子问题,且子问题比原问...
  • 写在前面的话:不是有递归函数复杂度就是O(nLogn)。 还是要具体问题具体分析。 如果递归函数中,只进行一次递归调用,递归的深度为depth; 在每个调用中时间复杂度为T,则总的时间复杂度为O(T*depth); double...
  • 递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。 解决方法 : 在递归算法中消除递归调用,使其转化为非递归算法。 1、采用一个用户定义的栈来模拟系统的递归调用工作栈...
  • 递归算法要素

    千次阅读 2020-10-14 16:17:26
    每次写递归,都按照这三要素来写,可以保证写出正确递归算法! 1. 确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么...
  • 递归算法的优缺点

    千次阅读 2021-03-31 20:27:25
    算法的优点 相对于迭代算法而言,结构更清晰、更简洁、更容易让人从宏观上理解...这里提一点,在编写递归算法程序的过程中,如果程序消耗的栈空间超出限制,通常会出现一个栈溢出的异常。StackOverflowException ...
  • 递归算法学习笔记

    2019-09-07 19:06:12
    直接或者间接调用自身的算法称为递归算法,用函数自身给出定义的函数称为递归函数。使用递归技术往往使函数的定义和算法的描述简捷且易于理解,有些数据结构,如二叉树等,由于其本身固有的递归特性,特别适合用递归...
  • 快速排序算法的原理和步骤: 首先进行分区,分区指的是从数组随机选取一个值,以其为轴,将比它小的放到它左边,比它大的值放到它右边。 确定指针,我们选取排除轴元素的数组最左(左指针)和最右(右指针)的元素。...
  • 递归算法知识点总结

    2020-05-23 17:37:46
    例如求n的阶乘等,问题的求解过程可以将其递归定义直接转换为对应的递归算法。 2.数据结构是递归的 如单链表等 3.问题的求解方法是递归的 如:是在有序数组中查找一个数据元素是否存在的折半查找算法 ...
  • 递归算法(对递归的理解,解题思路,适合新手参考)目录前言递归什么是递归怎么使用递归为什么用递归案例:青蛙爬楼梯更好的解法总结 目录 前言 本人是一个刚刚开始学习算法的新手,在我学习算法的过程中,总是对...
  • 总结:递归算法的一般思路

    千次阅读 2020-03-01 10:23:25
    递归算法 ** 程序直接或间接的调用自身的编程技巧称为递归算法,直接或间接调用自身的函数称为递归函数;它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。 递归问题基本思想:问题...
  • 递归的概念很简单,如果函数包含了对其自身的调用,该函数就是递归的。递归(Recursion),在数学与计算机科学中,是指在函数的定义中使用函数自身的方法。在使用递归时,需要注意以下几点:递归就是在过程或函数里...
  • 而对应的中文翻译 ”递归" 却表达了两个意思:”递"+”归"。 这两个意思,正是递归思想的精华所在。 从这层次上来看,中文翻译反而更达意。 递归是静中有动,有去有回。 循环是动静如一,有去无回。...
  • (1)假设二叉树采用链接存储方式存储,分别编写一个二叉树先序遍历的递归算法和非递归算法。 (2)一棵完全二叉树以顺序方式存储,设计一个递归算法,对该完全二叉树进 行中序遍历。 3.解题思路 (1)采用...
  • 递归算法

    2020-02-25 19:34:17
    引言: ...对于非尾递归和单项递归难以用直接转化法,但所有的递归算法理论上都可以使用间接转化法转化为等价的非递归算法。 常见递归算法示例  简单排序、  冒泡排序、  n皇后问题
  • 递归算法的总结

    2019-03-10 21:01:05
    1、递归的定义(什么是递归?) 在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。 2、递归思想的内涵(递归的精髓是什么?) 递归就是有去(递去)有回(归来) “有去”是指:递归...
  • Python实现递归算法

    千次阅读 2019-10-06 19:06:37
    递归概念任何可以用计算机求解的问题所需的计算时间都与其规模有关,问题的规模越小,解题所需的计算时间往往也越短,从而比较容易处理。直接或者间接调用自身的算法称为递归算法,用函数自身给出定义...
  • •缺点:递归算法的运行效率较低,无论是耗费的计算时间还是占用的存储空间都比非递归算法要多。 •解决方法:在递归算法中消除递归调用,使其转化为非递归算法。 ◦采用一个用户定义的栈来模拟系统的递归调用工作栈...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 120,672
精华内容 48,268
关键字:

关于递归算法不正确的是