搜索回溯算法(DFS)1------递归

目录

简介:

递归问题解题的思路模板

例题1:汉诺塔

例题2:合并两个有序链表

例题3:反转链表

例题4:两两交换链表中的节点

例题5:Pow(x,n)-快速幂

结语:


简介:

本系列将会带大家深入理解搜索中的一大分支深搜,深搜是离不开递归的和回溯思想的(优化需要剪枝),故我会在例题中详细指出解决这一系列问题的思考思路和解题技巧。

那么我们就从递归开始(深搜的基础)也就是本文中主要介绍的。

什么是递归?

简单来说就是函数自己调用自己。

为什么会用到递归?

大问题可以拆解成相同的子问题,且子问题的解法和大问题的一模一样,这是就可以用到递归。

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

a. 问题可以被划分为规模更⼩的⼦问题,并且这些⼦问题具有与原问题相同的解决⽅法。

b. 当我们知道规模更⼩的⼦问题(规模为n-1)的解时,我们可以直接计算出规模为n的问题的解。

c. 存在⼀种简单情况,或者说当问题的规模⾜够⼩时,我们可以直接求解问题。

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

a. 验证是否满⾜简单情况。

b. 假设较⼩规模的问题已经解决,解决当前问题。

上述步骤可以通过数学归纳法来证明。

如何理解递归?

不要太在意细节,相信函数。 

递归问题解题的思路模板

当然在设计递归函数之前最重要的是你要你的递归函数干嘛。

1.递归函数的作用

2.相同子问题------------函数头

3.只关心某一个子问题是如何解决的------------函数体

4.递归出口

建议友友在写递归类型的题目是一定要把这三个地方考虑清楚了再下手。

最后就是相信函数。

例题1:汉诺塔

链接:汉诺塔

题目简介:

递归问题非常经典的一道题目,在经典汉诺塔问题中,有 3 根柱子及 N 个不同大小的穿孔圆盘,盘子可以滑入任意一根柱子。一开始,所有盘子自上而下按升序依次套在第一根柱子上(即每一个盘子只能放在更大的盘子上面)。移动圆盘时受到以下限制:
(1) 每次只能移动一个盘子;
(2) 盘子只能从柱子顶端滑出移到下一根柱子;
(3) 盘子只能叠在比它大的盘子上。

请编写程序,用栈将所有盘子从第一根柱子移到最后一根柱子。

解法:

我们可以看到每次都可以把大问题分解成相同的子问题,且子问题的解决方法和大问题的一模一样故我们可以使用递归来处理。 

1.递归函数的作用

函数作⽤:将A中的上⾯n个盘⼦挪到C中。这里的A和C是函数参数的A和C不是实际的(很重要)。

2.相同子问题(函数头)

我们要设计一个函数头来完成汉诺塔的递归过程,我们可以看到我们需要三根柱子和要记录下来还剩下几个盘子。故我们的函数头可以设计成public void dfs(List<Integer> a, List<Integer> b, List<Integer> c, int n)。关于List<Integer>如果友友还没有学到的话可以把他看成一个数组。

3.只关心某一个子问题是如何解决的(函数体)

我们取第三层汉诺塔来研究(大问题被拆成3层汉诺塔)。

我们发现子问题刚来三件事

1.把A上面n - 1 个盘子通过C移动到B 

2.把A上面最后一个盘中移动到C

3.把B上面n - 1个盘中通过A 移动到C

dfs(a, c, b, n - 1);

c.add(a.remove(a.size() - 1));//这里是因为给出的例题这样做就是把盘中从A移动到C(理解思路即可)

dfs(b, a, c, n - 1);

4.递归出口

当问题的规模变为n=1时,即只有⼀个盘⼦时,我们可以直接将其从A柱移动到C柱。

本例题代码实现如下:

class Solution {
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
        dfs(A,B,C,A.size());
    }
    public void dfs(List<Integer> A, List<Integer> B, List<Integer> C,int n ){
        if(n == 1){
            C.add(A.remove(A.size() - 1));
            return;
        }
        dfs(A,C,B,n - 1);
        C.add(A.remove(A.size() - 1));
        dfs(B,A,C,n - 1);
    }
}

关于本例题要注意的点:c.add(a.remove(a.size() - 1));可能有的友友会纠结为什么不是remove(0)呢,其实自己模拟一下即可,我们移走盘子是从最上面那个盘子开始移动的。

不用太深究函数的细节(陷入将无法自拔),如果是第一次的话可以去b站上看看递归的全过程细节(这里的不用深究是建立在已经对它的展开有一定理解了,第一次学汉诺塔的话我还是建议大家可以看看别人推的整个过程,理解更加深刻)。

例题2:合并两个有序链表

链接:合并两个有序链表

题目简介:

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

解法:

通过分析题目我们发现可以把大问题拆成下图的子问题,上面的1已经合并有序现在要让2,4和下面链表合并有序。

故我们可以用递归来解决这道问题。

1.递归函数的作用

交给你两个链表的头结点,你帮我把它们合并起来,并且返回合并后的头结点.

2.相同子问题(函数头)

由上图可以看到函数要包含两个链表还要能返回合并后的链表故函数头设定为:public ListNode mergeTwoLists(ListNode l1, ListNode l2)

3.只关心某一个子问题是如何解决的(函数体)

选择两个头结点中较小的结点作为最终合并后的头结点,然后将剩下的链表交给递归函数去处理。

4.递归出口

当某⼀个链表为空的时候,返回另外⼀个链表。

代码如下:

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        if(list1 == null){
            return list2;
        }
        if(list2 == null){
            return list1;
        }
        if(list1.val <= list2.val){
            list1.next = mergeTwoLists(list1.next,list2);
            return list1;
        }else{
            list2.next = mergeTwoLists(list1,list2.next);
            return list2;
        }

    }
}

例题3:反转链表

链接:反转链表

题目简介:

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

解法:

要想让1到5逆序,可以让5逆序后让1到4逆序一直这样下去我们不难发现这就是递归。

1.递归函数的作用

交给你⼀个链表的头指针,你帮我逆序之后,返回逆序后的头结点。

2.相同子问题(函数头)

需要传入链表,5节点逆序后要把逆序后的新头节点返回故将函数体设为public ListNode reverseList(ListNode head)

3.只关心某一个子问题是如何解决的(函数体)

先把当前结点之后的链表逆序,逆序完之后,把当前结点添加到逆序后的链表后⾯即可。

4.递归出口

当前结点为空或者当前只有⼀个结点的时候,不⽤逆序,直接返回。

代码如下:

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode newHead = head;
        if(head == null){
            return head;
        }
        ListNode cur = head.next;
        ListNode prev = head;
        head.next = null;
        while(cur != null){
            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }
}

例题4:两两交换链表中的节点

链接:两两交换链表中的节点

题目简介:

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

 解法:

我们通过阅读题目可以发现要使整个链表两两交换 ,可以将大问题分为把1和2后面的节点两两交换,把一二交换即可,同理3和4也是同样的处理思路。

1.递归函数的作用

交给你⼀个链表,将这个链表两两交换⼀下,然后返回交换后的头结点;

2.相同子问题(函数头)

需要传入原始链表和返回新的头节点故设计为:public ListNode swapPairs(ListNode head)

3.只关心某一个子问题是如何解决的(函数体)

先去处理⼀下第⼆个结点往后的链表,然后再把当前的两个结点交换⼀下,连接上后面处理后的链表;

4.递归出口

当前结点为空或者当前只有⼀个结点的时候,不⽤交换,直接返回。

代码如下:

class Solution {
    public ListNode swapPairs(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode newHead = head.next;
        head.next = swapPairs(head.next.next);
        newHead.next = head;
        return newHead;
    }
}

例题5:Pow(x,n)-快速幂

链接:Pow(x,n)

题目简介:

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,xn )。

  解法:

这题不能使用1个1个数的分离递归(会超时),我们这里要采用二分的思路,具体实现如下:

1.递归函数的作用

求出x 的n 次⽅是多少,然后返回;

2.相同子问题(函数头)

需要传入需要n次方的x和n还要将其返回public double pow(double x, int n)

3.只关心某一个子问题是如何解决的(函数体)

先求出x 的n / 2 次⽅是多少,然后根据n 的奇偶,得出x 的n 次⽅是多少;

4.递归出口

当n 为0 的时候,返回1 即可。

代码如下:

最上面要区分一下正负数的区别即可。

class Solution {
    public double myPow(double x, int n) {
        return (n < 0) ? 1.0 / pow(x, - n) : pow(x , n);
    }
    public double pow(double x,int n){
        if(n == 0){
            return 1.0;
        }
        double tmp = pow(x,n / 2);
        return (n % 2 == 0) ? tmp * tmp : tmp * tmp * x;
    }
}

总结:本文章是搜索回溯的第一篇,带大家再复习了一下递归,后续的章节会带领大家深度理解深搜和回溯算法。

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/428763.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++_哈希表

目录 1、哈希表的用法 2、哈希函数-除留余数法 3、哈希冲突 4、闭散列和开散列 4.1 闭散列 4.1.1 哈希表的扩容 4.1.2 二次探测 4.2 开散列&#xff08;哈希桶&#xff09; 4.2.1 开散列的扩容 结语 前言-哈希表概念&#xff1a; 哈希表也是一种存储信息的结构&am…

数据结构(一)——概述

一、绪论 1.1数据结构的基本概念 数据&#xff1a;用来描述客观事物的数、计算机中是字符及所有能输入并被程序识别和处理的符号的集合。 数据元素&#xff1a;数据的基本单位&#xff0c;一个数据元素可由若干数据项组成。 数据结构&#xff1a;指相互之间存在一种或多种特…

“羊驼“入侵CV,美团浙大沈春华团队将LLaMA向CV扩展,构建全新基础模型VisionLLaMA

本文首发:AIWalker https://arxiv.org/abs/2403.00522 https://github.com/Meituan-AutoML/VisionLLaMA 本文概述 大型语言模型构建在基于Transformer的架构之上来处理文本输入, LLaMA 系列模型在众多开源实现中脱颖而出。类似LLaMa的Transformer可以用来处理2D图像吗&#xf…

FreeRTOS学习笔记-基于stm32f103(1)基础知识

一、裸机与RTOS 我们使用的32板子是裸机&#xff0c;又称前后台系统。裸机有如下缺点&#xff1a; 1、实时性差。只能一步一步执行任务&#xff0c;比如在一个while循环中&#xff0c;要想执行上一个任务&#xff0c;就必须把下面的任务执行完&#xff0c;循环一遍后才能执行…

【Java设计模式】三、简单工厂、工厂方法模式、抽象工厂模式

文章目录 0、案例&#xff1a;咖啡屋1、简单工厂模式 静态工厂&#xff08;不属于23种之列&#xff09;2、工厂方法模式3、抽象工厂模式4、简单工厂模式 配置文件解除耦合5、JDK源码中对工厂模式的应用 0、案例&#xff1a;咖啡屋 模拟咖啡店点餐。咖啡有多种&#xff0c;抽…

day6 数组 嵌套循环

1&#xff1a;打印杨辉三角 91 int arr[6][6];92 int i,j0;93 for(i0;i<6;i)94 {95 for(j0;j<i;j) 96 {97 if(j0||ij)98 {99 arr[i][j]1; …

14 数值稳定性 + 模型初始化和激活函数【李沐动手学深度学习v2笔记】

1. 数值稳定性 神经网络的梯度 向量对向量求导&#xff08;梯度&#xff09;得到矩阵&#xff0c;太多的矩阵进行乘法会导致常见的两个问题 梯度消失和梯度爆炸 MLP MLP使用ReLU作为激活函数 梯度爆炸的问题 输入很大的时候梯度接近为0 梯度消失 梯度消失的问题 只能训练比…

一个强大的 VS Code 的AI代码插件:Fitten Code

AI 代码助手有 GitHub Copilot&#xff0c;一直想用&#xff0c;但是要爬梯子&#xff0c;还要收费。 一款国产AI写代码神器 Fitten Code&#xff0c;满足了我对AI写代码的幻想。它在功能上基本和 GitHub Copilot 差不多。 重要是的是&#xff0c;免费&#xff0c;免费&#…

【Web - 框架 - Vue】随笔 - Vue的简单使用 - 快速上手

【Web - 框架 - Vue】随笔 - Vue的简单使用 - 快速上手 Vue模板代码 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>模板</title> </head> <body> <div></div>…

安装Docker及DockerCompose

0.安装Docker Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道…

09 Qt扩展LineEdit组件:Input输入框

系列文章目录 01 Qt自定义风格控件的基本原则-CSDN博客 02 从QLabel聊起&#xff1a;自定义控件扩展-图片控件-CSDN博客 03 从QLabel聊起&#xff1a;自定义控件扩展-文本控件-CSDN博客 04 自定义Button组件&#xff1a;令人抓狂的QToolButton文本图标居中问题-CSDN博客 0…

数仓项目6.0(一)

尚硅谷大数据项目【电商数仓6.0】企业数据仓库项目_bilibili 数据流转过程 用户➡️业务服务器➡️数据库存储➡️数仓统计分析➡️数据可视化 数据仓库处理流程&#xff1a;数据源➡️加工数据➡️统计筛选数据➡️分析数据 数据库不是为了数据仓库服务的&#xff0c;需要…

【开源】SpringBoot框架开发数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

MATLAB环境下基于离散小波变换的心电信号伪影去除及PQRST波检测

可穿戴个人健康监护系统被广泛认为是下一代健康监护技术的核心解决方案。监护设备不断地感知、获取、分析和存储大量人体在日常活动中的生理数据&#xff0c;为人体的健康状况提供必要的、准确的、集成的和长期的评估和反馈。在心电监测领域&#xff0c;可穿戴传感器具有以下应…

C及C++每日练习(1)

一.选择&#xff1a; 1.以下for循环的执行次数是&#xff08;&#xff09; for(int x 0, y 0; (y 123) && (x < 4); x); A.是无限循环 B.循环次数不定 C.4次 D.3次 对于循环&#xff0c;其组成部分可以四个部分&#xff1a; for(初始化;循环进行条件;调整) …

JavaScript实现的计时器效果

之前做过电商网站倒计时的效果&#xff0c;今天在倒计时的基础上&#xff0c;把代码修改了一下&#xff0c;改为计时器效果&#xff0c;实现了以下功能&#xff1a; 1.点击“开始”后&#xff0c;按秒计时且“开始”文字变为“停止”&#xff1b; 2.点击“停止”&#xff0c;计…

【Python实战】——Python+Opencv是实现车牌自动识别

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

自动化测试过程中的手机验证码处理!

手机验证码登录很普遍了&#xff0c;那么在自动化测试的时候需要登录&#xff0c;登录不了就意味着很多自动化就没法执行下去了。 到底该怎么处理呢&#xff1f;其实并不难&#xff0c;我们先看下验证码的业务逻辑&#xff0c;在我们“点击获取验证码”按钮的时候&#xff0c;…

LeetCode刷题-206.反转链表【递归实现】

206.反转链表 题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 示例1 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例2 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例3 输入&#xff1a;hea…

【C语言】动态内存管理------常见错误,以及经典笔试题分析,柔性数组【图文详解】

欢迎来CILMY23的博客喔&#xff0c;本篇为【C语言】动态内存管理------常见错误&#xff0c;以及经典笔试题分析&#xff0c;柔性数组【图文详解】&#xff0c;感谢观看&#xff0c;支持的可以给个一键三连&#xff0c;点赞关注收藏。 前言 在了解完内存操作中最关键的一节---动…