前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【刷题】初探递归算法 —— 消除恐惧

【刷题】初探递归算法 —— 消除恐惧

作者头像
叫我龙翔
发布2024-06-03 08:40:38
850
发布2024-06-03 08:40:38
举报
文章被收录于专栏:就业 C++ 综合学习

送给大家一句话:

有两种东西,

我对它们的思考越是深沉和持久,

它们在我心灵中唤起的惊奇和敬畏就会日新月异,

不断增长,

这就是我头上的星空和心中的道德定律。

-- 康德 《实践理性批判》

1 递归算法

在解决一个规模为 n 的问题时,如果满足以下条件,我们可以使用递归来解决:

  1. 问题可以被划分为规模更小的子问题,并且这些子问题具有与原问题相同的解决方法。
  2. 当我们知道规模更小的子问题(规模为 n-1)的解时,我们可以直接计算出规模为 n 的问题的解。
  3. 存在一种简单情况,或者说当问题的规模足够小时,我们可以直接求解问题。这里一般成为函数出口(非常重要)

一般的递归求解过程如下:

  1. 验证是否满足简单情况: 简单情况是指问题规模非常小,通常可以直接得到答案的情况。我们需要首先检查当前问题是否满足这种情况。
  2. 假设较小规模的问题已经解决,解决当前问题: 在递归中,我们假设已经解决了规模较小的子问题,然后基于这些子问题的解来构建当前问题的解。这种假设称为“递归假设”。

总结来说,递归代码的编写如同使用一个“黑盒”一样,我们需要相信递归调用会正确解决子问题,而我们只需要关注处理当前的问题。

下面我们通过一个具体实例来展示如何在实践中解决问题:

假设我们要计算斐波那契数列中的第 n 项。斐波那契数列的定义如下:

\text{F}(0) = 0
\text{F}(1) = 1

对于

n \geq 2

\text{F}(n) = \text{F}(n-1) + \text{F}(n-2)

在这个问题中: 简单情况是

\text{F}(0)

\text{F}(1)

,我们可以直接得到答案。 对于其他情况,我们假设

\text{F}(n-1)

\text{F}(n-2)

已经计算出来,然后通过

\text{F}(n) = \text{F}(n-1) + \text{F}(n-2)

计算出

\text{F}(n)

这种递归解决问题的方法非常强大,但也需要注意避免过度递归带来的性能问题,比如栈溢出或时间复杂度过高等。

接下来我们一起来解决问题吧!!!

2 Leetcode 面试题 08.06. 汉诺塔问题

上连接: 面试题 08.06. 汉诺塔问题

题目描述

汉诺塔是个非常有意思的问题,其典故更是神乎其神:

法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。

题目给我们了xyz三个容器模拟柱子,我们需要模拟实现移动的过程,将X容器中的盘子移动到Z中。

算法思路

乍一看我们想不到什么思路,所以我们先来画图分析一波:

通过这三种情况我们就能分析出来,这道题可以被拆分成许多子问题来解决:

如果想要移动n个盘子

  1. 先把 n - 1 个盘子移到中转柱子上,再把第n个移到目标柱子上。
  2. 接下来处理 这n - 1 个盘子,把 n - 2 个小盘子移到中转柱子上,第 n - 1个移动到目标柱子上。
  3. 重复 1 2 直到解决问题…

注意

代码语言:javascript
复制
    // x 为当前柱子 y 为中转柱子 z 为目标柱子 
    //我们只需要注意解决当前的问题,子问题交给黑盒处理
    void dfs(vector<int>& x, vector<int>& y, vector<int>& z , int n )
    {   
        //递归出口
        if(n == 1)
        {
            int tmp = x.back();
            z.push_back(tmp);
            x.pop_back();
            return ;
        }
        //将 n 个盘子从 X 转移到 Z 
        //则先把 n-1个盘子移到 Y
        dfs(x , z , y , n - 1);
        //然后此时X只有一个最大的盘子, 移到Z
        int tmp = x.back();
        z.push_back(tmp);
        x.pop_back();
        //现在 Y 上有 n-1 个盘子继续进行移动到Z上
        dfs(y , x , z , n - 1);
        return ;
    }
    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
        int n = A.size();
        dfs(A , B , C , n);
        return ;
    }

提交:过啦!!!

3 Leetcode 21. 合并两个有序链表

上连接!家人们:21. 合并两个有序链表

题目描述

很好理解的题目!

算法思路

相信大家看到这个题,肯定有迭代循环思路,但是今天我们通过递归来解决问题:

我们首先分析一下:

  • 当前问题:当我们处理当前情况时,我们需要把后续处理交给黑盒,我们需要的是将较小的节点插入到新链表中
  • 子问题:处理除去以被处理的“较小的节点”之外的链表节点,使其合并。
  • 函数出口:当我们处理到两个链表都为空时直接返回,或者一方为空直接返回另一链表即可!
代码语言:javascript
复制
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
    	//处理为空的情况
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        //都不为空,选择较小值进行插入!!!
        if(l1->val < l2->val)
        {
        	//l1 小 就把它的next交给黑盒处理
            l1->next = mergeTwoLists(l1->next , l2);
            //然后将它返回(这样黑盒才可以获取当前节点的next节点)
            return l1;
        }
        else
        {
        	//l2 小 就把它的next交给黑盒处理
            l2->next = mergeTwoLists(l1 , l2->next);
            return l2;
        }
    }

提交:过啦!!!

4 Leetcode 206. 反转链表

上链接: 206. 反转链表

题目描述

同样很好理解,接下来我们来使用递归解决问题

算法思路

首先这道题需要注意的一点是:我们要先找到新链表的头(即当前链表的尾节点)黑盒的返回值设置为新链表的头,然后再来进行反转。就类似二叉树的后序遍历。我们不能从链表的头开始反转到尾(先序遍历)。因为这样就无法获取新链表的头结点了

从宏观来看:我们只需要处当前问题:

  1. 子问题: 后续节点的反转!黑盒会返回我们的头结点。我们的黑盒一定可以帮助我们解决后序的节点的反转。
  2. 当前问题:把当前节点插入到以被反转的链表后,把当前节点的next设置为空即可!
  3. 函数出口:当走到链表结尾即为出口!
代码语言:javascript
复制
ListNode* reverseList(ListNode* head) {
        //寻找新的头结点
        if( head == nullptr || head->next == nullptr ) return head;
        ListNode* newhead = reverseList(head->next);
        //进行倒置
        head->next->next = head;
        //next设置为空
        head->next = nullptr;
		//返回新链表的头
        return newhead;
    }

提交:过啦!!!

5 Leetcode 24. 两两交换链表中的节点

跟上节奏:24. 两两交换链表中的节点 !!!

题目描述:

题目也很好理解奥

算法思路

我们依旧是使用递归来解决:

  1. 当前问题:置换两个节点,并指向后续以及置换完成的链表。
  2. 子问题:后序节点的置换
  3. 函数出口:为空或只有一个节点之间返回即可。
代码语言:javascript
复制
    ListNode* swapPairs(ListNode* head) {
        //利用递归解决问题
        //一次要处理两个节点
        if(head == nullptr) return nullptr;
        if(head->next == nullptr) return head;

        //继续处理 --- 相信 swapPairs(tmp) 这个会处理好剩余部分
        ListNode* tmp = swapPairs(head->next->next);
        //进行处理
        //记录下 1 2 节点的后面的2节点,它是置换后的头节点。
        ListNode* ret = head->next;
        //置换
        head->next->next = head;
        head->next = tmp;
        
        return ret;
    }

提交:过啦!!!

6 Leetcode 50. Pow(x, n)

最后一题:50. Pow(x, n) !!!

题目描述

这道题需要我们使用幂函数,当然不是一般的循环相乘(必然超时),我们要实现快速幂!

算法思路

快速幂的思想很简单:假如我们需要 210 ,我们就求25 ,求 25 就求 22 * 22 * 2 …以此类推直到次数为 0 就返回 1

需要注意的是负数的处理,求负次幂,我们可以先求正的然后在取倒数。 还有边界的处理,题目所给的最小值 - 231 取正后为 231,超出int的范围,所以需要转换为long long

代码语言:javascript
复制
    double myPow(double x, long long n) {
        if(n == 0) return 1;
        if( n < 0 ) 
        {
            long long t = (long long)(-n);
            double tmp = myPow( x , t / 2);
            return t % 2 == 0 ? 1 / (tmp * tmp) : 1 / (tmp * tmp * x);
        }
        else
        {
         double tmp = myPow( x ,  n / 2);
         return n % 2 == 0 ? tmp * tmp : tmp * tmp * x;
        }
      
    }

提交过啦!!!

7 总结

我们进行递归算法,只需要处理好当前问题,其余相信我们的黑盒可以解决。注意:

  1. 函数出口的设置,这个是关键!!!
  2. 返回值的设置要合适,看题分析!!!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 递归算法
  • 2 Leetcode 面试题 08.06. 汉诺塔问题
    • 题目描述
      • 算法思路
      • 3 Leetcode 21. 合并两个有序链表
        • 题目描述
          • 算法思路
          • 4 Leetcode 206. 反转链表
            • 题目描述
              • 算法思路
              • 5 Leetcode 24. 两两交换链表中的节点
                • 题目描述:
                  • 算法思路
                  • 6 Leetcode 50. Pow(x, n)
                    • 题目描述
                      • 算法思路
                      • 7 总结
                      • Thanks♪(・ω・)ノ谢谢阅读!!!
                      • 下一篇文章见!!!
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档