我爱学算法之—— 递归

一、汉诺塔问题

题目解析

经典汉诺塔问题:三根柱子(A、B、C),N个不同大小的圆盘。

初始状态:N个大小不同的圆盘按照大小(一个盘子上只能放比自己小的盘子)依次套在第一个柱子(A)上。

我们可以按照以下规则移动圆盘:

  1. 每次只能移动一个圆盘
  2. 每次只能移动柱子顶端的圆盘
  3. 圆盘只能放在比自己的圆盘上

结束状态:A柱子上的所有盘子,移动到C柱子上。

算法思路

这里N个圆盘(N可以是1,2,3,4,5...)

当 N 等于 1 时 : 将圆盘从A柱子移动到C柱子即可;

当 N 等于 2 时 : 将A柱上的 1 个圆盘先移动到B柱上,再将A柱上第2个圆盘移动到C柱上,最后将B柱上的圆盘移动到C柱上即可;

当 N 等于 3 时 : 将A柱上的 2 个圆盘移动到B柱上,再将A柱上第3个圆盘移动到C柱上,最后将B柱上的圆盘移动到C柱上即可;(N=2时,移动圆盘)

依次类推,可以发现,当 N > 2 时,将圆盘从A柱移动到C柱上,就只有三个步骤:

  • 现将A柱上的N-1个圆盘移动到B柱上
  • 再将A柱上的最后一个圆盘移动到C柱上
  • 最后将B柱上的N-1个圆盘移动到C柱上

所以,移动圆盘的整体逻辑就可以看成:

将X柱上的 N 个圆盘,借助Y柱,移动到Z柱上 (当N == 1时,直接将X柱上的圆盘移动到C柱上即可)

代码实现

cpp 复制代码
class Solution {
public:
    void dfs(vector<int>& x, vector<int>& y, vector<int>& z, int n) {
        if (n == 1) {
            z.push_back(x.back());
            x.pop_back();
            return;
        }
        // 将x柱 n-1个圆盘借助z柱 移动到 y柱上
        dfs(x, z, y, n - 1);
        // 将x柱 最后一个光盘移动到z柱
        z.push_back(x.back());
        x.pop_back();
        // 将y柱 n-1个圆盘借助x柱 移动到z柱上
        dfs(y, x, z, n - 1);
    }
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
        dfs(A, B, C, A.size());
    }
};

二、合并两个有序链表

题目解析

合并两个有序链表,这道题除了使用双指针,也可以使用递归来解决;

算法思路

合并链两个有序链表(升序),回想双指针遍历的过程,无非就是依次比较两个节点值的大小,然后修改指针指向。

cur1cur2指针遍历两个链表,将值较小的节点放入新链表中

  • cur1->val < cur2->val:将cur1节点放入新链表中,然后cur1指针向后遍历。(这个过程是不是可以看做:合并以cur1的下一个节点作为头结点和以cur2为头结点的两个链表,最后返回一个合并后新链表的头结点
  • cur1->val >= cur2->val:将cur2节点放入新链表中,然后cur2指针向后遍历。(这个过程是不是可以看做:合并以cur1作为头结点和以cur2的下一个节点作为头结点的两个链表,最后返回一个合并后新链表的头结点

所以,使用递归来解决这道题那就非常简单了:

dfs函数就是完成合并cur1cur2两个有序链表,最后返回合并后链表的头结点。

  • 判断cur1cur2是否为空
  • 判断cur1->valcur2->val 的大小关系,更新合并后链表的头结点
    1. 如果cur1->val < cur1->val : 合并cur1-nextcur2两个有序链表,返回新链表的头结点
    2. 否则,合并cur1cur2->next两个有序链表,返回新链表的头结点
  • 最后,返回合并后链表的头结点。

代码实现

cpp 复制代码
class Solution {
public:
    ListNode* dfs(ListNode* cur1, ListNode* cur2) {
        if (cur1 == nullptr)
            return cur2;
        if (cur2 == nullptr)
            return cur1;
        ListNode* ret = nullptr;
        if (cur1->val < cur2->val) {
            ret = cur1;
            ret->next = dfs(cur1->next, cur2);
        } else {
            ret = cur2;
            ret->next = dfs(cur1, cur2->next);
        }
        return ret;
    }
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        return dfs(list1, list2);
    }
};

三、反转链表

题目解析

给定一个单链表的头结点,我们要反转该链表,然后返回反转后的链表

算法思路

这道题可以说是非常easy了,我们可以使用三指针依次修改链表指向。

这里我们想,能否使用递归来解决呢?

要使用递归来解决,那递归函数要完成的功能就是:反转单链表,然后返回反转后链表的头结点

当链表为空或者只有一个节点时(node == nullptr || node->next == nullptr):直接返回node即可

当链表中节点个数大于等于2时:反转以node->next为根节点的头结点,然后将node节点放到新链表的结尾。

这里要将node放到新链表的结尾,而node->next就是反转后链表的最后一个节点。

这里直接获取反转后新链表的头结点,然后修改指针的指向即可

代码实现

cpp 复制代码
class Solution {
public:
    ListNode* dfs(ListNode* node) {
        if (node == nullptr || node->next == nullptr)
            return node;
        ListNode* rhead = dfs(node->next);
        node->next->next = node;
        node->next = nullptr;
        return rhead;
    }
    ListNode* reverseList(ListNode* head) { return dfs(head); }
};

四、两两交换链表中的节点

题目解析

给定一个单链表,我们要两两交换其中相邻的节点,最后返回交换后链表的头结点

算法思路

之前在学习链表相关的算法时,使用指针遍历链表,使用头插法交换两个相邻链表节点,最后返回新链表的头结点。

这里我们使用递归的思路来解决这道问题

根据上述使用递归来解决问题的思路,这道题的大致思路:递归完成后续节点的两两交换,然后进行当前节点nodenode->next节点的交换。

  • 如果node == nullptr || node->next == nullptr :直接返回即可
  • 递归完成后续链表的相邻节点的两两交换,获取后续链表交换后的头结点
  • 完成nodenode->next节点的交换,记录交换后的头结点,然后返回。

代码实现

cpp 复制代码
class Solution {
public:
    ListNode* dfs(ListNode* node)
    {
        if(node == nullptr || node->next == nullptr)
            return node;
        // 两两交换后续链表,获取交换后的头结点
        ListNode* next = dfs(node->next->next);
        // 交换node和node->next节点
        ListNode* prev = node;
        ListNode* cur = node->next;
        cur->next = prev;
        prev->next = next;
        // 返回交换后的头结点
        return cur;
    }
    ListNode* swapPairs(ListNode* head) {
        return dfs(head);
    }
};

五、Pow(x, n)

题目解析

这道题要实现pow(x,n),计算x的n次幂(n是正整数)

算法思路

要求xn次幂,可以说是每次简单的

无非就是递归求xn-1次幂,然后再乘上x返回即可;但是这样未免也太复杂了些。

这里我们可以每次计算xn/2次幂,然后将结果相乘,就可以求出xn次幂;

细节

n是奇数时时,那xn/2次幂乘上xn/2次幂就是xn-1次幂,计算结果不正确;这里就需要进行特殊判断:

  • 如果n%2 != 0 : 返回tmp*tmp*x
  • 如果n%2 == 0 : 返回tmp*tmp

注意 : 这道题中n是可以<0的,而在递归逻辑中是当n == 0时返回1;这里就要进行特殊处理:当n<0时,就计算x / 1.0abs(n)次幂。

代码实现

cpp 复制代码
class Solution {
public:
    double dfs(double x, int n) {
        if (n == 0)
            return 1;
        double tmp = dfs(x, n / 2);
        return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;
    }
    double myPow(double x, int n) {
        long long cnt = n;
        if (n < 0) {
            x = 1.0 / x;
            cnt = -cnt;
        }
        return dfs(x, cnt);
    }
};

六、计算布尔二叉树的值

题目解析

一个完整二叉树,叶子节点值为01FalseTrue)、非叶子节点的值为23(逻辑或、逻辑与)

要遍历给定的完整二叉树,然后计算根节点的布尔运算值,最后返回。

算法思路

这道题整体思路还是非常简单的,采用后续遍历,获取左右子树的布尔运算值,然后计算,最后返回即可。

代码实现

cpp 复制代码
class Solution {
public:
    bool dfs(TreeNode* root)
    {
        if(root->val == 0)
            return false;
        if(root->val == 1)
            return true;
        if(root->val == 2)
            return dfs(root->left) || dfs(root->right);
        if(root->val == 3)
            return dfs(root->left) && dfs(root->right);
        return false;
    }
    bool evaluateTree(TreeNode* root) {
        if(root == nullptr)
            return false;
        return dfs(root);
    }
};

本篇文章到这里就结束了,感谢支持

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws

相关推荐
chao1898445 分钟前
基于改进二进制粒子群算法的含需求响应机组组合问题MATLAB实现
开发语言·算法·matlab
Imxyk12 分钟前
P9242 [蓝桥杯 2023 省 B] 接龙数列
c++·算法·图论
炽烈小老头16 分钟前
【每天学习一点算法 2026/04/10】Excel表列序号
学习·算法
郝学胜-神的一滴16 分钟前
二叉树后序遍历:从递归到非递归的优雅实现
数据结构·c++·程序人生·算法·
宝贝儿好19 分钟前
【LLM】第一章:分词算法BPE、WordPiece、Unigram、分词工具jieba
人工智能·python·深度学习·神经网络·算法·语言模型·自然语言处理
渡我白衣20 分钟前
运筹帷幄——在线学习与实时预测系统
人工智能·深度学习·神经网络·学习·算法·机器学习·caffe
colus_SEU21 分钟前
SVM 的终极视角:合页损失函数 (Hinge Loss) 与正则化
算法·机器学习·支持向量机
汀、人工智能22 分钟前
[特殊字符] 第71课:爬楼梯
数据结构·算法·数据库架构·图论·bfs·爬楼梯
MicroTech202523 分钟前
微算法科技(NASDAQ :MLGO)量子启发式算法与CNN、Transformer结合,实现端到端彩色图像分割
科技·算法·启发式算法
X journey24 分钟前
机器学习进阶(14):交叉验证
人工智能·算法·机器学习