递归(带例题)

1.什么是递归

一个函数调用它本身就是递归。递归通常把一个大型复杂的问题层层转化为子问题,直到到子问题无需进一步递归就可以解决的地步。递归极大地降低了代码量。

通常来讲一个递归算法由以下部分组成:

  • 能够不使用递归来产生答案的终止方案
  • 可将所有其他情况拆分到基本案例

2.递归的作用

递归算法在以下情况下有着很大的作用:

  • 数据的定义是按递归定义的
  • 问题解法按递归算法实现
  • 数据的结构形式是按递归定义的

3.递归的应用

以上文中递归适用的三种情况来帮助大家对递归有一个更好的理解


3.1 当数据的定义是按递归定义的时

最常见的就是Fibonacci数列

F~0~ = 0 ; F~1~ = 1; F~n~ = F~n-1~ + F~n-2~ , n$\geq$ 2

比如说我们现在要求出Fibonacci数列的第5个数字,根据上文提到的递归的组成部分,我们将递归函数的组成分为两部分:

  • 当传入的实参是0时返回0,当传入的实参时1时返回1(终止方案)
  • 其他情况函数递归调用自己(拆分)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flwOuEed-1644237481659)(C:\Users\lemer\Desktop\digui.png)]

递归算法会将f(5)逐步拆分成f(1) + f(0) + f(1) + f(1) + f(0) + f(1) + f(0) + f(1)

写成程序是这样的:

#include<iostream>
using namespace std;

int Fibonacci(int n) {
    if (n == 0) //终止方案
        return 0;  
    if (n == 1) //终止方案
        return 1;  
    return Fibonacci(n - 1) + Fibonacci(n - 2); //拆分
}

int main() {
    cout << Fibonacci(5);
    return 0;
}

3.2 问题解法按递归算法实现时

思考下面的问题:把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放, 问共有多少种不同的分法?(5,1,1和1,5,1 是同一种分法。)

问题分析:

1、确定参数:题目有效数据只有苹果和盘子,在构造递归函数时不妨设m个苹果放在n个盘子里放法总数是f(i, j)

2、终止方案:(1)当没有苹果时所有盘子为空,返回1;(2)当没有盘子时返回0

3、拆分:(1)当苹果数m小于盘子数n时必定有盘子为空,且空盘子数至少为n - m,此情况的结果等同与将m个苹果放入m个盘子;(2)当苹果数m大于等于盘子数n时分两种情况:有盘子空和没有盘子空。表示有盘子空可以在递归时将盘子的数量减1但苹果数不变;表示没有盘子空可以先将每个盘子里预放一个苹果,再在递归调用函数时盘子数量不变,但是苹果数量变成原苹果数量减盘子数量。

写成程序是这样的:

#include<iostream>
using namespace std;

int f(int m, int n) {
    if (m == 0) //终止方案
        return 1;
    if (n == 0) //终止方案
        return 0;
    if (n > m) //拆分
        return f(m, m);
    return f(m, n - 1) + f(m - n, n); //拆分
}

int main() {
    int m, n;
    cin >> m >> n;
    cout << f(m, n) << endl;    
    return 0;
}

3.3 数据的结构形式是按递归定义的时

如二叉树的遍历、广度优先搜索等问题

现在给定一个二叉树,要求找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

问题分析:

(1)终止条件:遍历到的节点是空节点

(2)拆分:向当前节点的左子树和右子树遍历,每遍历一次深度加一,每次返回值取其左子树和右子树的最大深度值

#include<iostream>
using namespace std;

typedef struct BTree
{
    int    value;
    struct BTree* lchild;
    struct BTree* rchild;
}BTree;

BTree* CreateBTree(BTree* node, int* num, int& index)
{
    if (num[index] == 0)
        return NULL;
    else
    {
        node = new BTree;
        node->value = num[index];
        node->lchild = CreateBTree(node->lchild, num, ++index);
        node->rchild = CreateBTree(node->rchild, num, ++index);
    }
    return node;
}
void preOrder(BTree* root)
{
    if (root == NULL)
        return;
    cout << root->value << " ";
    preOrder(root->lchild);       //递归 左子树
    preOrder(root->rchild);       //递归 右子树   
}
int getdepth(BTree* root)
{
    if (root == NULL) //终止条件
        return 0;
    int lchild_depth = getdepth(root->lchild);
    int rchild_depth = getdepth(root->rchild);
    return max(lchild_depth, rchild_depth) + 1; //拆分
}
int main()
{
    int num[] = { 1,2,4,8,0,0,9,0,0,5,10,0,0,11,0,0,3,6,12,0,0,13,0,0,7,14,0,0,15,0,0 };
    BTree* root = NULL;
    int index = 0;
    root = CreateBTree(root, num, index);
    cout << "前序遍历为:\n";
    preOrder(root);
    cout << endl;
    cout << "最大深度为: ";
    cout << getdepth(root);
    return 0;
}

4.递归的优缺点及其优化

4.1 优点

在一些特定问题中递归比循环更简洁,递归像一个黑盒子,编写时只需要关注边界条件和拆分公式

4.2 缺点

(1)递归是对函数的调用,需要占用栈空间,时间和空间消耗较大

(2)如果不对递归函数做一定的记忆化处理的话会有很多的重复运算,浪费时间和空间

(3)调用栈可能会溢出

4.3 优化(记忆化)

我们看到3.1的例子代码,当n取一个很大的值时会让程序做很多次重复的计算,我们这时可以采用空间换取时间的思想,用一个表记录算出来过的值,每次运算时先在表里找该值有没有被算过,如果有直接用之前算出来的值,如果没有就算出来后将其存入表中

这时代码实现:

class Solution {
public:
    unordered_map<int, int>hash;  //空间换时间
    int fib(int n) {
        if(n == 0) return 0;
        if(n == 1) return 1;
        if(hash[n]) return hash[n];  //算之前查找有没有被算出来过
        return hash[n] = fib(n - 1) + fib(n - 2); //没有的话把算出来的值存起来
    }
};

5.总结

看到了这里的话相信大家对递归有了一个更好的理解,这里有两个题目可以帮助加深大家对递归的理解:

力扣 70 爬楼梯;力扣 104 二叉树的最大深度

今天的分享就到这里,我们下期再见!

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇