C++ Recursion

递归是计算机领域的一个重要的概念。

所谓的递归, 直白的说, 就是函数调用自己实现(Functions call themselves)。 可以用递归解决的经典问题就是求Fibonacci 数列求和问题。

递归的定义有两部分组成:

(1)base case, 也就是产生集合中其他元素的基本元素, base case 是递归必须的。 用递归方法解决问题的时候, 必须明确base case是什么, 这是我们解决问题的关键。没有base case 的所谓的“递归”会无限运行下去, 直至我们的我们的程序run out of stack memory, 造成 stack overflow. 程序crash down。 例如下面的程序是错误的(crash down):

#include <iostream>
using namespace std;

void foo() {
   cout << "**" << endl;
   foo();
}

int main() {
   foo();
   return 0;
}


 

(2).rules thatreduce all other cases toward the base case. 翻译为: 由基本元素或者已有的对象产生新对象的构造规则。


明白了这递归的含义, 说说Fibonacci sequence, 数列的第n项的构造规则为:

fib(n) = fib(n -1 ) + fib(n -2), 

其中base case 为 fib(0) = 1, fib(1) = 1。

求出Fibonacci 第n 项的C++ 实现函数如下:

int fibonacci(int n) {
   if(n == 0||n == 1) {  //base case
      return 1;
   }
   else {
      return fibonacci(n - 2) + fibonacci(n - 1);
   }
}

一个完整的测试程序如下:

#include <iostream>

using namespace std;

int fibonacci(int n) {
   if(n == 0||n == 1) {  //base case
      return 1;
   }
   else {
      return fibonacci(n - 2) + fibonacci(n - 1);
   }
}

int main() {
   cout << fibonacci(4) << endl;

   return 0;
}
运行结果如下:

 

再举一个计算factorial 的例子:

#include <iostream>

using namespace std;

int factorial(int x) {
   if(x == 1) return 1; // base case
   return x * factorial(x -1);
}
int main()
{
    cout << "5! = " << factorial(5) << endl;
    return 0;
}


运行结果如下:

另外, 我们可以使用迭代的办法计算上面的n factorial problem。


递归算法思想清晰易懂。 但是容易带来效率的问题. 与迭代算法的比较如下:




使用递归的办法解决Fibonacci(n)的时间复杂度为exponential time O(2^n)。 如何改进呢?

generally, 改进的办法有三种:

(1)迭代(iteration)

(2) dynamic programming(动态规划)

(3)other strategies

对于Fibonacci , 我们可以使用动态规划。


下面给出另一个个可以用递归解决问题的例子:

Towers of Hanoi(汉诺塔)(问题解决的步骤数目记为T(n)) //n disks

3个pegs 如下, rule: disks从下到上面积递增。  小的disk必须在大的disk 之上, 否则就unbalanced。 最开始均位于peg A 之上。 我们的目标就是通过使用中介peg B, 将peg A 的所有的disk均移动到peg C上而不违背rule。 




下面举个例子说明解决思路:

N = 2  : 

step 1 :


 得到:


step 2:



step 3:



N = 3 disks:


 

step1: place  1 to C:


step 2: place  disk 2 to B:

step 3: move disk 1 from C to B:


step 4: move  disk 3 from A to C:


step: 5: move disk one from B to A:

step 6: move disk 2 from B to C:


step7:  move disk 1 from A to C:


于是, 我们花费了7步1去解决了这个问题。

那么如果disk 数目为4, 5 甚至更大的时候, 该如何办呢? 需要多少步解决呢?

算法如下(复杂度: T(n) = T(n-1) + 1 + T(n-1) = 2 T(n -1 ) + 1.):



例如N = 4, 我们已经知道可以到达下面的状态:


接下来, 将nth disk 移动到C中:

接下来, 我们可以使用A作为intermediate peg, 将B的3个disk 移动到C上面:



关于上述问题的递归的观察:

N = 2:


N = 3:(T(3)) step 1:  we have known how to move 2 disk from A to B(T(2))


得到:


step2: move 1 disk(largest) from A to C (1 step):


sep3:  we of course know how to move 2 disks from B to C usiing A(T(2)):



所以我们得到如下公式:

T(n) = T(n-1) + 1 + T(n-1) = 2 T(n -1 ) + 1.

不难求解得到:

T(n) = 2 ^n  - 1


程序如下:

#include <iostream>

using namespace std;

void HanoiMove(int, int, int, int);

int main() {
    int num;

    cout << "Enter the number of disks: ";
    cin >> num;
    cout << endl;
    HanoiMove(num, 1, 3, 2);

    return 0;
}

void HanoiMove(int Count, int n1, int n3, int n2) {
    if (Count > 0) {
        HanoiMove(Count - 1, n1, n2, n3);
        cout << "Move disk " << Count << " from " << n1 << " to " << n3
             << "." << endl;
        HanoiMove(Count - 1, n2, n3, n1);
    }

}

运行结果如下:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值