不走心的c++复建——递归与循环

不走心的c++复健

递归与循环

1. 递归算法

  如果一个问题需要重复多次计算相同的问题,通常可以考虑使用递归进行求解。换句话说,当一个问题可以分解为多个子问题,而这些子问题的求解方式相同,那么这个问题就可以使用递归进行解决。递归通过在一个函数的内部调用这个函数自身,经典的问题是Fibonacci序列的计算问题:
问题描述: 写一个函数,输入 n n n,求Fibonacci序列的第 n n n项。Fibonacci序列的定义如下:

f ( n ) = { 0 , n = 0 1 , n = 1 f ( n − 1 ) + f ( n − 2 ) , e l s e f(n)= \begin{cases} 0, n=0 \\ 1, n=1 \\ f(n-1)+f(n-2), else \end{cases} f(n)=0,n=01,n=1f(n1)+f(n2),else

根据上式中的推导公式,可以很轻易地写出如下的代码。

// Fibanacci array: f(n) = f(n-1) + f(n-2), f(0) = 0, f(1) = 1
int fibobacci(int n)
{
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    return fibobacci(n - 1) + fibobacci(n - 2);
}

  然而,递归算法为人诟病的一点就是高计算资源的消耗,以及引起栈溢出的高风险。每一次进行更深一层的递归,都需要对当前执行函数的返回地址以及局部变量进行压栈缓存,使得后续递归完成时函数还能继续运行。但是每一个进程的栈容量有限,当迭代次数过多,就会超出栈的容量,出现栈溢出的错误。
  对于Fibonacci问题而言,递归算法不是最好的解决方式,使用循环进行进行解决更合适。接下来简要谈一谈在什么情况下适合将递归问题用循环的方式进行解决,总结的也许不全面,欢迎各位留言补充,持更中。

2. 循环

  在上节中提过,递归适用于该问题可以分解为多个子问题的场景。然而,当这些子问题之间互相有重叠的部分——某个子问题的解依赖于其他子问题的解,使用递归算法进行求解时会进行大量重复的运算,造成计算资源的浪费。此时,该问题便不再适合使用递归进行求解。递归和循环的使用场景可以使用如下的方式进行总结:

if (子问题的求解不依赖于其他子问题)
    return 递归;
if (子问题依赖于其他子问题的解)
    return 将递归算法转变为循环的方式;

  递归算法往往采用由上往下的思考方式来解决问题,这样的方式容易对问题进行建模,即列出推导公式。在列出推导公式之后,再使用循环的实现方式由下往上地进行解决,即可以避免重复计算,也可以很容易地计算目标的函数 f ( n ) f(n) f(n)。Fibonacci问题可以先计算 f ( 0 ) , f ( 1 ) , f ( 2 ) f(0), f(1), f(2) f(0),f(1),f(2),依据子问题间的关系可以轻易计算出 f ( n ) f(n) f(n),代码如下。

int fibonacci(int n)
{
    if (n == 0)
        return 0;
    else if (n == 1)
        return  1;

    int tmp_1st = 1, tmp_2nd = 0;
    int res = 0;

    for (size_t i = 2; i <= n; i++)
    {
        res = (tmp_1st + tmp_2nd) % 1000000007; // 避免溢出
        tmp_2nd = tmp_1st;
        tmp_1st = res;
    }
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值