递归

作者: 欧阳_z | 来源:发表于2020-06-07 14:35 被阅读0次

1、最简单的一个递归,以相反的顺序打印字符串

void print_reverse (const char * s)
{
    if (NULL == s || '\0' == *s)
        return;
    print_reverse (s + 1);
    putchar( *s );
}

2、原地反转字符串

void reverse_str (char s[], int n)
{
    if (NULL == s)
        return;
    char tmp;
    if(n >= 2)
    {
        tmp = s[0];
        s[0] = s[n-1];
        s[n-1] = tmp;
        reverse_str(s+1, n-2);
    }
}

3、求斐波那契数列的第 n 项
数列的定义如下:
f(0) = 0;
f(1) = 1;
f(n) = f(n - 1) + f(n - 2); ( n > 1)
先用递归的写法:

func f(n int) int {
    if n < 2 {
        return n
    } else {
        return f(n - 1) + f(n - 2)
    }
}

再改为循环的写法:

func f(n int) int {
    if n < 2 {
        return n
    }
    var f0, f1, fn int
    f0,f1 = 0,1
    for i := 1; i < n; i++ {
        fn = f0 + f1
        f0 = f1
        f1 = fn
    }
    return fn
}

因为GoPython可以同时给多个变量赋值,所以可以这样简写:

func f(n int) int {
    if n < 2 {
        return n
    }
    f0, f1 := 0, 1
    for i := 1; i < n; i++ {
        f0, f1 = f1, f0 + f1
    }
    return f1
}

为什么要改成循环的写法呢?因为这里用递归会有大量重复计算:

                        f(5)
             f(4)         +        f(3)
        f(3)   +    f(2)       f(2)  +  f(1)
   f(2)  +  f(1)  f(1)+f(0)  f(1)+f(0)
f(1)+f(0)

展开之后相当于一棵二叉树,总的计算次数取决于二叉树的高度,以及每层的节点数,下面简单的计算一下:

高度的问题:
因为最左边每次只减 1,深度最深,大约是 n
最右边每次减 2,深度最浅,大约是n/2
而中间的部分介于两者之间,所以取值范围是n/2 <= h <= n

每层的数量的问题:
第 1 层只有 1 个,也就是20
第 2 层有 2 个,即 21
第 3 层有 4 个,即 22
第 k 层有 2 k-1 次方。

所以当 h=n,总的结点数为
S_n = {2^0} +{2^1} +{2^2} +... +{2^{n-1}}
根据等比数列求和公式,

S_n = \frac{a_1(1 - q^n)}{1-q} = \frac{1*(1 - 2^n)}{1-2} = 2^n - 1

h=n/2,总的结点数为
S_{(\frac{n}{2})} = {2^0} +{2^1} +{2^2} +... +{2^{\frac{n}{2}-1}}
根据等比数列求和公式,
S_{(\frac{n}{2})} = \frac{a_1(1 - {q^{\frac{n}{2}}})}{1-q} = \frac{1*(1 - {2^{\frac{n}{2}}})}{1-2} = {2^{\frac{n}{2}}} - 1
所以,如果用递归的方法,时间复杂度就介于 O(2n) 和 O(2n/2) 之间;
而用循环的方法,时间复杂度是 O(n),所以需要使用循环来写。

4、求 x 的 n 次幂
递归的写法:

func myPow(x float64, n int) float64 {
    if n == 0 {
        return 1
    }
    if n < 0 {
        return 1 / myPow(x , -n)
    }
    if n % 2 == 1 {
        return x * myPow(x , n-1)
    }
    return myPow(x*x , n/2)
}

模 2 和除以 2 都可以改为位运算,递归改为循环的写法:

func myPow(x float64, n int) float64 {
    if n < 0 {
        x = 1/x
        n = -n
    }
    pow := 1.0
    for n > 0 {
        if n & 1 == 1 {
            pow *= x
        }
        x *= x
        n >>=1
    }
    return pow
}

5、细胞分裂问题

(1)描述
有1个细胞,每1个小时分裂1次,1次分裂出1个子细胞,每个细胞能活3个小时。
求第n个小时有多少个细胞?
(2)分析
假设细胞在每个小时分别是:状态a,状态b,状态c;

设 f(n) 为第 n 小时的细胞数量,

设 fa(n) 为第 n 小时的 a 细胞数量,

设 fb(n) 为第 n 小时的 b 细胞数量,

设 fc(n) 为第 n 小时的 c 细胞数量,

则 f(n) = fa(n) + fb(n) + fc(n)。

fa(n) = fa(n-1)+ fb(n-1) + fc(n-1),当 n = 1,fa(1) = 1

fb(n) = fa(n-1),当 n=1,fb(n) = 0;

fc(n) = fb(n-1),当 n=1,2,fc(n) = 0;

f(n) = fa(n) + fb(n) + fc(n)

(3)实现

#include <stdio.h>

int fa(int n);
//  n 小时 b 阶段的细胞数量
int fb(int n)
{
    if(n==1)
    {
        return 0;
    }
    else
    {
        return fa(n-1);
    }
    
}

//  n 小时 c 阶段的细胞数量
int fc(int n)
{
    if(n==1 || n==2)
    {
        return 0;
    }
    else
    {
        return fb(n-1);
    }
}

//  n 小时 a 阶段的细胞数量
int fa(int n)
{
    if(n==1)
    {
        return 1;
    }
    else
    {
        return fa(n-1)+fb(n-1)+fc(n-1);
    }
}

int f(int n)
{
    return fa(n) + fb(n) + fc(n);
}

int main(void)
{
    printf("f( %d ) = %d \n", 1, f(1) );
    printf("f( %d ) = %d \n", 2, f(2) );
    printf("f( %d ) = %d \n", 3, f(3) );
    printf("f( %d ) = %d \n", 4, f(4) );
    return 0;
}

相关文章

  • 二叉树遍历

    先序遍历——[递归、非递归] 中序遍历——[递归、非递归] 后序遍历——[递归、非递归] 层次遍历——[递归、非递归]

  • 二叉树的遍历

    先序递归: 非递归: 中序递归: 非递归: 后序递归: 非递归 层次遍历

  • 二叉树的前序、中序、后序遍历(递归、非递归)

    二叉树 前序 递归: 非递归: 中序 递归: 非递归: 层序 递归: 非递归:

  • 树的遍历,golang实现

    先序,递归 中序,递归 后序,递归 先序,非递归 中序,非递归 后序,非递归 层序遍历

  • 3 递归(19)(方法层面的高级循环)

    递归 树的递归 其它递归

  • 递归,递归,递归

    在我告诉你什么是递归之前,你应该读一下这篇文章:递归,递归,递归。 如果你没有这么做,那么表扬一下自己。如果你那么...

  • 数据结构-树的遍历

    1. 先序遍历 递归实现 非递归实现 2. 中序遍历 递归实现 非递归实现 3. 后序遍历 递归实现 非递归实现 ...

  • 树的遍历

    节点结构: 先序遍历 递归 非递归 后序遍历 递归 非递归 中序遍历 递归 非递归 层序遍历 类库 有了上述遍历算...

  • 算法图解系列之递归[03]

    3 递归 3.1 递归<函数> 3.2 基线条件和递归条件 3.3 递归调用栈

  • 三十八、递归

    一、递归的概述 递归,指在当前方法内调用自己的这种现象。 递归分为两种,直接递归和间接递归。 直接递归称为方法自身...

网友评论

      本文标题:递归

      本文链接:https://www.haomeiwen.com/subject/ejyftktx.html