递归的作用小结-C++实现几个经典的递归程序

递归的作用

1. 用递归来完成递推

方法:

  • 求解目标:把关注点放在要求解的目标上。
  • 关系:找到第n次与第n-1次之间的关系。
  • 初始值:确定第1次返回结果。
1.1 n的阶乘

1的阶乘:1! = 1;
2的阶乘:2! = 2 x 1;

n的阶乘:n! = n x (n-1) x … x 2 x 1.


#include <iostream>
using namespace std;
// 下面的函数实现n的阶乘
int factorial(int n)
{
    if (n == 1) // 如果n=1,则阶乘为1
    {
        return 1;
    }
    else    // 如果n!=1, 则阶乘为n乘以(n-1)的阶乘。
    {
        return (n * factorial(n-1));
    }
}
int main()
{
    int n = 0;
    cout << "输入数字, 得到它的阶乘:" << endl;
    // 不断从控制台读取数据,直到读入Ctrl + z为止。
    while (cin >> n)
    {
        cout << factorial(n) << endl;
    }
    return 0;
}

1.2 斐波那契数列

斐波那契数列指的是这样一个数列:1, 1, 2, 3, 5, 8, 13, 21, 34 …
递推公式为:
F(1) = 1;
F(2) = 1;
F(n) = F(n-1) + F(n-2).


#include <iostream>
using namespace std;
// 本函数实现斐波那契数列
int fibonacci(int n)
{
    if (n ==1)
        return 1;
    else if (n == 2)
        return 1;
    else
        return (fibonacci(n-1) + fibonacci(n-2));
}
int main()
{
    int n = 0;
    cout << "输入斐波那契数列的序号:" << endl;
    // 不断从控制台读取数据,直到读入Ctrl + z为止。
    while (cin >> n)
    {
        cout << fibonacci(n) << endl;
    }
    return 0;
}

2. 模拟连续发生的动作

方法:

  • 连续动作:搞清楚连续发生的动作是什么。
  • 关系: 搞清楚不同动作之间的关系。
  • 边界条件:搞清楚边界条件。
2.1 十进制转二进制

这里我使用的方法是:除2取余,逆序排列。
给一个十进制的整数,一直除以2取余数,直到商为0。然后将所有的余数逆序排列,即为对应的二进制数。
例如:
123 == 1111011
123 / 2 = 61…余1
61 / 2 = 30…余1
30 / 2 = 15…余0
15 / 2 = 7…余1
7 / 2 = 3…余1
3 / 2 = 1…余1
1 / 2 = 0…余1


#include <iostream>
using namespace std;
// 下面的函数实现十进制转二进制
void decimal_to_binary(int n)
{
    if ((n/2) == 0)
    {
        cout << n;
    }
    else
    {
        // 注意下面两行代码不能调换顺序
        // 逆序排列余数,生成二进制
        decimal_to_binary(n/2);
        cout << (n % 2);
    }
    
}
int main()
{
    int n = 0;
    cout << "输入十进制数,将其转化为二进制数:" << endl;
    // 不断从控制台读取数据,直到读入Ctrl + z为止。
    while (cin >> n)
    {
        decimal_to_binary(n);
        cout << endl;
    }
    return 0;
}
2.2 汉诺塔问题

汉诺塔传说:
汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
具体问题:
有三根相邻的柱子,标号为A, B, C,A柱子上从下到上按金字塔状叠放着n个不同大小的圆盘,要把所有盘子一个一个移动到柱子C上,并且每次移动同一根柱子上都不能出现大盘子在小盘子上方,请问要如何移动?
汉诺塔初始状态

解决方法:
最简单的情况:
如果只有一个圆盘,则直接从A移到C即可,A -> C。
最复杂的情况:
如果有n个圆盘,则可以分解三步:
(1) 将A柱子上的上面n-1个圆盘搬到B柱子上;
(2) 再将A柱子上的第n个圆盘移动到C柱子上。
(3) 最后将B柱子上的n-1个圆盘移动到C柱子上。
如下图所示:
A上的n-1个圆盘移动到B上
A上的第n个圆盘移动到C上
最终状态

#include <iostream>
using namespace std;
void hanoi_tower(int n, char a, char b, char c);
void move(int numDisk, char a, char b, char c);
int main(){
    int n = 0;
    cout << "请输入圆盘数量:" << endl;
    while (cin >> n){
        int count = 0;  // 保存移动次数
        char a = 'A', b = 'B', c = 'C';
        // 将n个盘子从A经过B移动到C
        hanoi_tower(n, a, b, c);
        cout << endl;
    }
    
    return 0;
}
// 下面的函数实现汉诺塔问题
// 将n个盘子从A经过B移动到C
void hanoi_tower(int n, char a, char b, char c){
    if (n == 1){
        move(1, a, b, c);
    }
    else {
        hanoi_tower(n-1, a, c, b);
        move(n, a, b, c);
        hanoi_tower(n-1, b, a, c);
    }
}
// 将盘子从A移动到C
void move(int numDisk, char a, char b, char c)
{
    cout << a << " -> " << c << endl;
}

3. 进行“自动的分析”

方法:

  • 先假设,有一个函数能给出答案。
  • 在利用这个函数的前提下,分析如何解决问题。
  • 搞清楚最简单的情况下,答案是什么。
3.1 波兰表达式 (前缀表达式)

波兰表达式描述:
波兰表达式是一种把运算符前置的算术表达式:

  • 如 2 + 3 的波兰表达式为 + 2 3
  • 如 (2 + 3) * 4 的波兰表达式为 * + 2 3 4

我们需要编程求解出包含 + - * / 四个运算符的波兰表达式的值。
示例1:
输入:* + 11.0 12.0 + 24.0 35.0
输出:1357.0
示例2:
输入:* / + 12 36 + 1 3 - 15 8
输出:84

解决方法:

  1. 假设有一个函数calculator()可以计算波兰表达式。
  2. 分析波兰表达式,可以得到解答步骤。那么在当遇到运算符时,调用函数。如下所示:
    ‘+’: return calculator() + calculator();
    ‘-’: return calculator() - calculator();
    ‘*’: return calculator() * calculator();
    ‘/’: return calculator() / calculator();
    当遇到数值时,调用函数如下:
    return atof(str);
#include <iostream>
using namespace std;
// 下面函数可以求解波兰表达式
double calculator()
{
    char str[10];
    cin >> str;
    switch (str[0])
    {
    case '+':
        return calculator() + calculator();
    case '-':
        return calculator() - calculator();
    case '*':
        return calculator() * calculator();
    case '/':
        return calculator() / calculator();
    default:
        return atof(str);   // 将字符串转化为数值
    }
}
int main()
{
    cout << calculator();
    return 0;
}
3.2 放苹果

描述:
把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,问有多少种不同的分法?(所有的苹果都一样,所有的盘子都一样。)
注意:5, 1, 1 和 1, 5, 1 是同一种分法,与排列顺序无关。
示例1:
输入:7 3
输出:8
示例2:
输入:100 100
输出:190569292

解决方案:

  1. 假设有个函数count(m, n)可以解决放苹果问题。
  2. 分析:因为盘子都是一样的,所以不是排列问题,而是苹果的组合问题。苹果的数量是M,盘子的数量是N。分两种情况讨论:
    (1) 当M < N时,多余的盘子都不影响苹果的分法。所以可以得出count(m, n) == count(m, m);
    (2) 当M >= N时,又分两种情况:一是有空盘子的情况,则至少有一个空盘子,空盘子去掉不会影响苹果分法,因此count(m, n) ==count(m, n-1)。二是没有空盘子的情况,则每个盘子里面至少有一个苹果,去掉每个盘子里的那一个苹果,不会影响苹果分法,因此count(m, n) == count(m-n, n)。
  3. 边界条件:当M <= 1或者N <= 1时,只有一种分法,因此count(m, n) == 1。
#include <iostream>
using namespace std;
// 下面函数能够解决放苹果问题
int count(int m, int n)
{
    // 边界条件
    // 当M <= 1或者N <= 1时,只有一种分法,因此count(m, n) == 1。
    if (m <= 1 || n <= 1)
    {
        return 1;
    }
    // 当M < N时,多余的盘子都不影响苹果的分法。
    // 所以可以得出count(m, n) == count(m, m);
    if (m < n)
    {
        return count(m, m);
    }
    // 当M >= N时,又分两种情况:
    // 一是有空盘子的情况,则至少有一个空盘子,空盘子去掉不会影响苹果分法,
    // 因此count(m, n) == count(m, n-1)。
    // 二是没有空盘子的情况,则每个盘子里面至少有一个苹果,去掉每个盘子里的那一个苹果,
    // 不会影响苹果分法,因此count(m, n) == count(m-n, n)。
    else 
    {
        return (count(m, n-1) + count(m-n, n));
    }
}
int main()
{
    int m = 0, n = 0;
    cin >> m >> n;
    cout << count(m, n) << endl;
    return 0;
}

说明

上面三个场景没有明显的界限,道理都是相通的,不用刻意划分界限。
参考:Coursera课程《C程序设计进阶》

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我来给您讲解一下如何用程序实现0-1背包问题。 0-1背包问题的解法有很多种,其中归解法是一种比较简单易懂的解法。0-1背包问题是指:有n个物品和一个容量为m的背包,物品i的重量为w[i],价值为v[i],求将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。 首先,我们需要定义一个函数来解决这个问题,函数的定义如下: int knapsack(int n, int m, int w[], int v[]) 其中,n表示物品的个数,m表示背包的容量,w[]表示每个物品的重量,v[]表示每个物品的价值。 接下来,我们来看看如何实现这个函数。 首先,我们需要考虑归的终止条件。当背包容量为0或者物品数量为0时,归结束,返回0。 然后,我们需要考虑每个物品放或者不放的情况。如果当前物品的重量大于背包容量,那么只能不放;如果当前物品可以放入背包,那么我们需要比较放入当前物品和不放当前物品两种情况下的价值,取其中较大的一个。 实现代码如下: int knapsack(int n, int m, int w[], int v[]) { if (n == 0 || m == 0) { // 终止条件 return 0; } if (w[n - 1] > m) { // 当前物品的重量大于背包容量,只能不放 return knapsack(n - 1, m, w, v); } else { // 可以放或者不放 return max(knapsack(n - 1, m, w, v), knapsack(n - 1, m - w[n - 1], w, v) + v[n - 1]); } } 其中,max函数表示取两个值中较大的一个。 这样,我们就可以用程序实现0-1背包问题了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值