zoukankan      html  css  js  c++  java
  • 递归的讨论

    递归的讨论

             递归是一个强大的工具,用递归写的程序往往比较容易理解和实现。但是当面对一些递归性问题的时候,我们的第一感觉就是用递归程序实现,但是从问题到最终的实现程序之间需要经过什么?怎样才能写出正确的递归程序?我们将在这里进行关于递归的讨论。

             我们首先介绍两个简单的递归实现程序,然后讨论循环与递归的关系再结合之前递归程序,讨论如何才能写出正确的递归程序

    一、两个简单的递归程序

    这里我们讨论阶乘的计算和斐波那契数列的计算。

     

    首先我们给出这两个的非递归实现:

    // 阶乘与斐波那契数列的非递归实现
    #include <iostream>
    using namespace std;
    
    int fact(int n)
    {
        if (n == 0)
        {
            return 1;
        }
        int ret = 1;
        for (int i = 1; i != n+1; ++i)
        {
            ret *= i;
        }
        return ret;
    }
    
    int fibo(int n)
    {
        if (n == 0 || n == 1)
        {
            return n;
        }
        int a = 0, b = 1;
        int ret = 0;
        for (int i = 2; i != n+1; ++i)
        {
            ret = a + b;
            a = b;
            b = ret;
        }
        return ret;
    }
    
    int main()
    {
        cout << fact(5) << endl;
        cout << fibo(5) << endl;
        system("PAUSE");
        return 0;
    }

     

             以上关于阶乘与斐波那契数列的非递归实现分别用了循环实现。

             下面我们讨论阶乘与斐波那契数列的递归实现。

             阶乘的递归定义为:

    fact(n) = n * fact(n-1)

           n = 01时,fact(0) = fact(1) = 1

             斐波那契数列的递归定义为:

           fibo(n) = fibo(n-1) + fibo(n-2)

           n=0时,fibo(0) = 0; n=1时,fibo(1) = 1

             根据阶乘和斐波那契数列的递归定义,我们给出其递归程序实现:

    // 阶乘和斐波那契数列的递归实现
    #include <iostream>
    using namespace std;
    
    int fact(int n)
    {
        if (n == 0 || n == 1)
        {
            return 1;
        }
        return n * fact(n-1);
    }
    
    int fibo(int n)
    {
        if (n == 0 || n == 1)
        {
            return n;
        }
        return fibo(n-1) + fibo(n-2);
    }
    
    int main()
    {
        cout << fact(5) << endl;
        cout << fibo(5) << endl;
        system("PAUSE");
        return 0;
    }

     

    一个问题能够用递归实现需要满足两个条件:

    1)、存在递归关系,这是递归的先决条件

          2)、存在终止条件,否则程序无法终止

     

             二、递归与循环的关系

    从前面可以看出,阶乘和斐波那契数列的非递归实现分别包含了一个循环;而其递归实现里并没有循环结构,而仅是对其自身的递归调用。递归调用从某种意义上说相当于一个循环。一个递归调用,相当于一个循环。

     

             三、递归的进一步讨论

             前面我们有几篇是涉及递归实现的问题:

             如何生成升序序列

                       从三组0-9中依次选择一个数字,并保持3个数字是非降序的

             从每组中依次选择一个元素

                       从三组0-9中依次选择一个数字,不要求这3个数字的升降序

             组合序列、排列序列的生成实现

                       生成集合的组合序列和排列序列

             1)、如何生成升序序列

             定义:A(N, >X) = M * A(N-1, >X+1),当N=0时,终止

             递归实现:1、一个循环;2、一个递归调用

             2)、从每组中依次选择一个元素

             定义:A(N) = M * A(N-1),当N=0时,终止

             递归实现:1、一个循环;2、一个递归调用

             3)、组合序列、排列序列的生成实现

                      组合序列

                       定义:C(N, M) = C(N-1, M-1) + C(N-1, M),当M=0时,终止

                       实现:1、无循环;2、两个递归调用

                       排列序列

                       定义:A(N) = N * A(N-1),当N=0时,终止

                       实现:1、一个循环;2、一个递归调用

             前面我们讨论了递归与循环的关系,一个递归调用相当于一个循环。在解决某种问题的递归实现时,如何判定是否有循环?

             这里有两种方式,第一种是看递归定义,如果递归定义中只有自身的,则递归实现中没有循环,只有递归调用。如果递归定义中除了自身,还有其他变量,则递归实现中有对应于变量的循环以及关于自身的递归调用。

             第二种方法是看问题原型,如果原型中只有一个循环,那么递归实现中只需要一个递归调用,不需要循环结构。如果原型中有两个循环,那么递归实现种除了一个递归调用外,还需要一个循环结构。

     

             四、如何才能写出正确的递归程序

             如果一个问题符合递归定义,那么就可以用递归方式实现。用递归程序解决问题的步骤归纳如下:

             1)、首先明确问题的描述

             2)、给出递归定义和终止条件

             3)、根据递归定义决定递归调用和循环结构

             4)、设计递归函数接口

             5)、实现、测试

     

             五、总结

             这里我们讨论了关于递归程序如何书写,主要从问题的递归定义出发,看其定义是否包括变量和自身,再决定递归函数的定义结构即除了递归调用外是否还包含循环结构。

             最后,我们归纳了如何才能写出正确的递归程序,主要分为5个步骤进行。在以后的工作中,如果碰到需要递归才能解决的问题,我们的第一出发点就是要给出其递归定义(而不是直接code)。先思考,再编程。

  • 相关阅读:
    c-复习基础
    java-根据起止IP获取IP段集合
    java-随机数
    java-数组
    TypeSafe Config使用
    日志手段
    git 常用命令
    看门狗
    容器HashSet原理(学习)
    容器Vector原理(学习)
  • 原文地址:https://www.cnblogs.com/unixfy/p/3161932.html
Copyright © 2011-2022 走看看