递归思想以及晕递归的解决方案

什么情况下可以用递归的思想

一个问题可以被分为多个子问题,且子问题之间不冲突,并且多个子问题解决后,问题也被解决了。就可以用递归。

递归的思路

递归首先有一个返回条件,就是说函数肯定不能无限递归下去,那么就要有一个在问题规模较小的时候可以判断的结束条件。
其次递归分为“超级操作”和“微操作”,超级操作就是递归函数本身,而微操作一般都是“递”和“归”中的“归”过程。

如何使用递归函数

这里举个例子,就拿最典型的汉诺塔举例。

汉诺塔问题

问题描述:有从小到大的n个圆盘,圆盘按照从上至下从小到大的顺序套在柱子A上,另有柱子B,C
1.将所有圆盘转移至柱子C
2.圆盘顺序不变且必须时刻保证小圆盘在大圆盘之上
3.一次只能移动一个圆盘

这里将三根柱子分别定义为src,buf,tar。
从src转移至tar。
首先src只有一个圆盘的情况,只需要将src给tar即可,这里记为f(1)操作 。
src有两个圆盘的时候,就要借助buf缓冲,先把最小的放在buf,然后将第二个放到tar,再将最小的从buf拿到tar。这里记为f(2)。
当圆盘数量增加到3个,就可以看做是大规模情况了,那么我们怎么把三个圆盘的问题分解呢,可以把上面两个用f(2)操作,转移到buf,再把第三个移到tar,再对buf上的两个执行f(2)操作,单独转移就是f(1)操作嘛,到这里把整个过程连起来就是f(2),f(1),f(2);
那么4个,5个···n个圆盘,不就是f(n-1) ,f(1), f(n-1)的操作一直执行了吗,当然这里要注意,第一个f(n-1)和第二个f(n-1)他们的函数逻辑不变,但是柱子目标会改变。
这里贴上代码

// 汉诺塔问题
// 问题描述:有从小到大的n个圆盘,圆盘按照从上至下从小到大的顺序套在柱子A上,另有柱子B,C
//          1.将所有圆盘转移至柱子C
//          2.圆盘顺序不变且必须时刻保证小圆盘在大圆盘之上
//          3.一次只能移动一个圆盘
#include <iostream>
#include <vector>
using namespace std;
void move(vector<int>& src, vector<int>& tar) {
    int pan = src.back();
    src.pop_back();
    tar.push_back(pan);
}

void dfs(int i, vector<int>& src, vector<int>& buf, vector<int>& tar) {
    if(i == 1) {
        move(src, tar);
        return;
    }
    dfs(i - 1, src, tar, buf);
    move(src, tar);
    dfs(i - 1, buf, src, tar);
}

int main() {
    // 有序数组n=10的圆盘
    vector<int> vec;
    for(int i = 0; i < 10; ++i) {
        vec.push_back(i);
    }
    vector<int> buf, tar;
    dfs(10, vec, buf, tar);
    for(int i : tar) {
        cout << i << ' ';
    }
    return 0;
}

想不通,仔细一想就晕,怎么办,让我们来治治晕递归。

当然这里我已经会了,也懒得写,直接把我在哪学来的贴上得了。
参考资料:https://www.youtube.com/watch?v=kEWQj2Hb8kc
(这里哔站也有,哔站搜五点七边应该就出来了,再次鸣谢这位大佬)
https://www.hello-algo.com/chapter_divide_and_conquer/hanota_problem/#3

posted @ 2024-08-08 14:52  ThirteenQ  阅读(10)  评论(0编辑  收藏  举报