递归的深层次理解+初始搜索算法

news2024/11/19 2:49:22

1)什么是递归:就是函数自己调用自己的过程

2)为什么会使用到递归:主问题->相同的子问题,相同的子问题->一样相同的子问题

3)递归结束的条件:也就是细节和出口最小的不能在继续进行分割的子问题

4)宏观看待递归的过程:

1)不要在意递归展开的细节图

2)那递归的函数看成是一个黑盒

3)相信这个黑盒一定可以完成这个任务

3.1)进行二叉树遍历的这个函数就是来针对于当前根节点进行后续遍历的,相信这个函数一定是可以完成二叉树的后续遍历的,进行写代码的时候,不要过多的考虑这个函数实现的内部细节,当成一个黑盒,先进行遍历根节点,再进行遍历左子树和右子树;

针对于二叉树的后序遍历来说先进行遍历根节点,在进行遍历这棵树的左子树和右子树;

void dfs(Node* root){
//注意递归结束的条件,是划分这个子问题的最小单元,也就是递归到叶子节点
 if(root==null) return null;
  dfs(root.left);//遍历这棵树的左子树
  dfs(root.right);//遍历这棵树的右子树
  printf(root.val);//打印根节点
}

3.2)针对于快排上来说这个函数是让指定的数组的给定区间区间有序,参数是(传入数组,传入左边的区间,在传入右边的区间)目的是让左边的区间到右边的区间有序;

具体的步骤是先找到中间节点,划分出一个区间,先让左边的区间(从开始位置到基准值)有序,再让右边的区间(从基准值到右边的区间)有序而这个让左区间有序和右区间有序,正好是我们所写的这个函数的任务(让数组的某一段区间有序),这样就划分除了子问题,还是要把这个我们自己所写的函数看成是一个黑盒,并且相信这个黑盒一定可以完成这个任务;

之前学过快排,让左段区间有序,还要从左段区间中找出一个基准值,继续进行划分

让右端区间有序,就需要从右端区间找出一个基准值,在进行继续划分,不断地进行子问题的划分;

void quicksort(int left,int right,int[] array){//这个函数的目的就是为了让数组的某一段区间有序
   if(left>=right)//划分子问题的最小区间,只有一个元素,本身就是有序的
int privot=getPrivot(left,right,array);
quicksort(left,privot,array);//让左区间有序
//这个函数就是为了让这个数组的某一段区间有序,此时我们是让这个数组的left到privot区间是有序的,我们相信这个函数一定可以完成这个任务,并且把这个函数看作是一个黑盒
quicksort(privot+1,right,array);//让右区间有序;

3.3)针对于并排序来说,我们写一个函数实现归并排序,这个函数的目的就是为了实现归并排序,也就是让数组的某一段区间有序,整体思路就是先划分数组的中间元素,从左端点到中间元素排序的,从中间元素到右端点排序的,左区间有序,有区间有序,那么在整体合并两个有序数组即可,终止条件就是数组没有元素或者只有一个元素;

   void merage(int left,int right,int[] array){//这个函数的目的就是为了让数组的某一段区间有序
        if(left>=right) return ;
        int mid=(left+right)/2;
        merage(left,mid,array);//先进行归并排序左区间
        merage(mid+1,right,array);//再进行归并排序右区间
        sort(left,right,array);//先让左区间有序,再让右区间有序,在整体上进行排序)
}

5)如何写好递归

5.1)先找到相同的子问题,函数头的参数的传递和某一个子问题是一模一样的,归并排序的子问题就是给定数组的一段区间,把数组进行排一下序就可以了,二叉树的后续遍历是,跟定根节点进行后续遍历,正好适合函数头的功能是一模一样的;

5.2)只关心某一个子问题是如何解决了,这就涉及到了函数体的书写

5.3)避免写出死递归,写出口的时候,只关心子问题到那里的时候是不可以进行分割了,接下里就来看将数据结构中二叉树的题,在使用宏观方式理解一下

搜索:就是为了查找值

1)前面是深度优先遍历,后面是宽度优先遍历

2)遍历只是一种形式,目的是为了搜索,搜索是为了把所有的情况列举出来的时候,有可能是一个树状的形式,有可能是一个图状的形式,把所有的情况都给进行暴力的遍历一遍的时候,其实就是一个搜索,暴力枚举所有的结果,把所有可能出现的情况都给遍历一遍;

搜索=dfs(深度优先遍历和递归有关)+bfs(宽度优先遍历)

拿出最经典的问题,全排列问题:题目就是类似于说,有1 2 3三个数,总共可以排列出多少种不同的情况:第一个格子放1......高中的排列组合问题;

通过深度优先搜索就可以进行遍历所有的排序序列,也可以用宽度优先遍历来进行遍历所有的情况,把每一层的结果存放到一个队列里面

就比如说我再进行深度优先遍历的时候,找到了123所在的位置,但是此时并没有找到我所想要的数,此时就回退到1号位置即可,所以说深搜的时候是一定会涉及到回溯的,因为在进行深搜的过程中是一定会回退到上一级的,你返回到上一层的时候实际上就是一个回溯

回溯与减枝 

1)回溯:就是深度优先遍历,就是再进行尝试某一种结果的时候,在进行寻找解决问题的某一种情况的时候,发现某一种情况行不通,行不通的时候,于是就返回退回到上一级,从上一级继续开始尝试;

2)以走迷宫的例子来进行理解一下:假设当前从起点出发,只要是有拐点,就分成两种情况,要么是向左走,要么是向右走,如果向左走,就一直向左走,直接一条路走到黑,如果发现向左侧走走不通的话,就直接返回到那个决策的点,下面中红色的线代表的是回溯 

3)剪枝:在回溯岔路口上有若干种选择,但是我们明确当前要选择的路已经不是我们最终想要的结果,我们就可以将这种结果给剪切掉,这就是剪枝

当我两条路都走过的时候,回溯到园点的时候,此时有两条路可以走,但是这两条路我都已经走过了,而且还发现根本走不通,此时就可以得出结论,这两条路都不用再走了,当出现这两条路都不用再继续走的情况下,就是剪支;

一)汉诺塔:

题目要求:在移动盘子和生成最终结果的过程中,要注意的是大的盘子是不可以罗列在小的盘子上面的

面试题 08.06. 汉诺塔问题 - 力扣(Leetcode)

解决汉诺塔问题的根本思路:

1)先把A盘子上面的N-1个盘子借助C盘子移动到B盘子上面先把A上面的N-1个盘子转移到B上面;

2)把A上面的最大的盘子直接移动到C上面

3)最后再将B上面的N-1个盘子借助C盘子移动到A上

1)当我们解决一个问题的时候发现某一个问题更小的子问题可以使用相同的方式来进行解决的话,此时就可以使用递归来解决

2)当解决一个大问题的时候,又出现了一个相同类型的子问题,解决这个大问题的方式和解决这个子问题的方式是一模一样的,当我们继续解决这个子问题的时候,又发现了相同类型的子问题,此时就可以使用递归地解决,都是从一个位置开始借助另一个位置放到最终的位置上面;

1)重复子问题==函数头

1)上面的重复子问题就是1 2 3步,重复子问题名称:把X柱子上面的一堆盘子,借助y柱子,转移到z柱子上面;

2)void dfs(x,y,z,int n),你传入N个柱子,我就可以把X上面的N个盘子,借助Y盘子的帮助,转移到Z上面一开始我们要解决的问题就是要把A上面的个柱子借助B柱子转移到C柱子上;

2)只关心某一个子问题在做什么,设计函数体

class Solution {
    //主函数是为了将A的N个盘子借助B移动到C上
    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;
        }else{
dfs(A,C,B,n-1);
//先将A主子上面的N-1个盘子从A借助C移动到B上,和主函数做的事情是相同的
C.add(A.remove(A.size()-1));
//里面的remove函数是一个下标,直接将A剩下的最后一个大盘子移动到C上
dfs(B,A,C,n-1);
//最后将B上面的N-1个盘子从B开始借助A移动到C上面
        }
    }
    public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
          dfs(A,B,C,A.size());
    }
}

3)递归的出口:是哪一个子问题不可以再继续划分

当N=1的时候,直接将X的盘子放到Z上面

二)合并两个有序链表:

剑指 Offer 25. 合并两个排序的链表 - 力扣(Leetcode)

在做递归的题的时候一定要找到重复子问题

解题思路:重复子问题+宏观的看待问题

1)重复子问题:函数头的设计:Node dfs(Node node1,Node node2)

把这个函数头当成一个黑盒,相信他一定可以完成这个任务

这里面的重复子问题就是合并两个有序链表,当在进行合并两个有序链表的时候,先找到两个链表中最小的节点充当头节点,再继续合并两个有序链表,最终将头节点+后面合并到一起的链表;

2)只是关心某一个问题在做什么:

2.1)首先进行比较两个传入的链表的较小值

2.2)选择较小的值来充当头节点

2.3)让头节点进行连接剩下的两个链表合并的结果,两个链表是如何进行合并的并不关心,只是相信这个函数一定可以帮助我完成这个操作;

2.4)直接返回头节点

3)递归的出口:子问题不能再继续划分if(l1==null)||(l2==null),谁为空返回另一个

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //1.找到函数出口
        if(l1==null) return l2;
        else if(l2==null) return l1;
        //先找到两个链表的头结点的较小值
        else if(l1.val>=l2.val){
            l2.next=mergeTwoLists(l1,l2.next);//最小值的节点.next(两个子链表合并起来的节点)
            return l2;
          }
        else {
//把这个函数看成是一个黑盒,并相信这个函数一定可以完成这个任务
          l1.next=mergeTwoLists(l1.next,l2);
          return l1;
        }
    }
}

递归VS循环(迭代)之间的关系:

递归:是否存在重复子问题

循环:也是在不断地重复执行子问题

所以循环和递归是可以相互之间转换的

递归VS深搜之间的关系:

1)递归的展开图和树的优先遍历是一样的,也就是说模拟递归的执行过程其实就是对一棵树做一次深度优先遍历,也被称之为是DFS,递归的展开图,其实就是对一棵树做一次深度优先遍历;

2)例如如果你想要将这个递归转化成循环,你需要借助一个栈来进行帮助,原因就是因为当我们进行递归展开根节点的左子树的时候,当前根节点并没有执行完成,没有执行完的意思是说我还没有展开当前根节点的右子树部分,所以需要用到栈来保存根节点的信息,方便于左子树打印完成之后,能够找到根节点信息,进而去调用右子树,所以就需要栈来保存根节点的信息

3)中序遍历只存在于二叉树中

public class HelloWorld {
    public static void print(int[] array,int i){
        if(i==array.length) return;
        System.out.println(array[i]);
        i++;
        print(array,i);
    }
    public static void main(String[] args) {
        int[] array=new int[]{1,2,3,4,5,6,7,8,9,10};
        print(array,0);
    }
}

先序遍历VS后序遍历: 

1)按照顺序打印是先进行打印数字,然后再打印下一层(递归调用)

2)按照反序打印是先进行打印下一层(递归调用),等到递归回退到函数上一层的时候再来进行打印数字

反转链表:

剑指 Offer II 024. 反转链表 - 力扣(Leetcode)

1)​​​​​​​先将当前头节点后面的链表先进行逆置,并且把逆置后的链表头结点返回(如果只是将前两个链表进行逆置,那么根本无法找到第三个节点)

2)让当前节点添加到逆置之后的链表的后面的节点即可

3)这个递归问题的退出入口是当进行遍历到叶子节点或者是尾节点或者是当前节点是空

结束条件:当链表中已经没有节点就可以结束了,这个时候结束循环

思路:可以将链表看作成是一棵树,其实将下面这个链表做一次后续遍历就可以了,首先有一个dfs函数在不断地进行向下遍历,当遇到空节点或者是叶子节点,此时这个叶子节点就是链表反转之后的头节点,此时在这个函数中将newhead节点进行返回,此时newhead节点是不断返回到上一层;

假设说现在从第5层返回到了第四层,在本层是有能力接收到下层的返回值的(也就是下一层的节点),将下一个节点的next域指向当前函数的节点,将自己的next置为空,不断向上回退

于是就成功地完成了链表反转的操作;

class Solution {
    public ListNode reverseList(ListNode head) {
//0.当链表中只有一个头结点况且链表为空就不要进行反转了
      if(head==null||head.next==null) return head;
//1.首先将head后面的链表进行翻转,把这个代码看成是一个黑盒并相信这个代码一定可以完成这个任务
       ListNode newHead=reverseList(head.next);
//2.将头节点拼接上去
       head.next.next=head;
//3.头节点要置成null
       head.next=null;
//4.返回新的头节点
       return newHead;
    }
}

其实head.next才是最终的判断条件,newHead在进行向下遍历的过程回退中其实是永远指向的是原链表中的最后一个节点最后向上回退的过程中也是不断地指向链表中的最后一个节点

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全面分析MySQL?ERROR?1045出现的原因及解决

目录 通常从网上都能找到解决方案 1.停止服务2.跳过验证3.修改密码4.重启服务原因分析 SO 解决办法 在命令行输入mysql -u root –p&#xff0c;输入密码&#xff0c;或通过工具连接数据库时&#xff0c;经常出现下面的错误信息&#xff0c;相信该错误信息很多人在使用MySQL…

使用Kali Linux 暴力破解wifi密码详细步骤

使用Kali Linux 暴力破解wifi密码详细步骤所谓暴力破解就是穷举法&#xff0c;将密码字典中每一个密码依次去与握手包中的密码进行匹配&#xff0c;直到匹配成功。所以能否成功破解wifi密码取决于密码字典本身是否包含了这个密码。破解的时间取决于CPU的运算速度以及密码本身的…

信息安全-数据安全-数据安全平台建设实践

背景 在大数据时代&#xff0c;数据已经成为公司的核心竞争力。此前&#xff0c;我们介绍了美团酒旅起源数据治理平台的建设与实践&#xff0c;主要是通过各种数据分析挖掘手段&#xff0c;为公司发展决策和业务开展提供数据支持。 近期&#xff0c;业内数据安全事件频发&…

日本社交网络市场:Facebook 与本土巨头的竞争分析

摘要&#xff1a;Facebook作为全球最大的社交媒体平台之一&#xff0c;成功地击败了日本的本土社交网站&#xff0c;包括Mixi、DeNA和Gree等。 社交媒体在全球范围内迅速崛起&#xff0c;改变了人们的社交方式和信息传播方式。然而&#xff0c;在不同国家和地区&#xff0c;由于…

尚硅谷Docker实战教程-笔记08【高级篇,Docker复杂安装详说】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【基础篇&#xff0c;Docker理念简介、官网介绍、平台入门图解、平台架构图解】…

易基因: DNA甲基化差异水平分析中的DMC、DMR、DMG鉴定 | 干货分享

大家好&#xff0c;这里是专注表观组学十余年&#xff0c;领跑多组学科研服务的易基因。 DNA甲基化&#xff08;DNA methylation&#xff09;为DNA化学修饰的一种形式&#xff0c;能够在不改变DNA序列的前提下&#xff0c;改变遗传表现。所谓DNA甲基化是指在DNA甲基化转移酶的…

跑深度学习nvidia驱动忽然实效的详细解决方法

由于经常跑深度学习&#xff0c;所以对于显卡驱动什么的都还是整的比较明白的不含糊&#xff0c;所以都能跑的起来。但是今天跑pytorch框架时&#xff08;用到cuda&#xff09;忽然给我报了个错&#xff1a; RuntimeError: No CUDA GPUs are available这给我整不会了&#xff…

HarmonyOS学习路之开发篇—数据管理(轻量级数据存储)

轻量级数据存储概述 轻量级数据存储适用于对Key-Value结构的数据进行存取和持久化操作。应用获取某个轻量级存储对象后&#xff0c;该存储对象中的数据将会被缓存在内存中&#xff0c;以便应用获得更快的数据存取速度。应用也可以将缓存的数据再次写回文本文件中进行持久化存储…

设计模式之装饰者模式-TS中装饰器介绍

装饰器的基本介绍 装饰器是一种特殊类型的声明&#xff0c;它能够被附加到类声明&#xff0c;方法&#xff0c;访问符&#xff0c;属性或参数上。 装饰器使用expression这种形式&#xff0c;expression求值后必须为一个函数&#xff0c;它会在运行时被调用&#xff0c;被装饰的…

LoRA原理解析

文章目录 前言现有方案存在的问题Adapter TuningPrefix Tuning LoRA总结 前言 随着模型规模的不断扩大&#xff0c;微调模型的所有参数&#xff08;所谓full fine-tuning&#xff09;的可行性变得越来越低。以GPT-3的175B参数为例&#xff0c;每增加一个新领域就需要完整微调一…

界面组件DevExpress ASP.NET Core v23.1新版亮点 - 增强的数据可视化

DevExpress ASP.NET Core Controls使用强大的混合方法&#xff0c;结合现代企业Web开发工具所期望的所有功能。该套件通过ASP.NET Razor标记和服务器端ASP.NET Core Web API的生产力和简便性&#xff0c;提供客户端JavaScript的性能和灵活性。ThemeBuilder工具和集成的Material…

纯LiDAR Odometry——LinK3D论文详解

先说总结 总体思路是&#xff1a;针对输入来的点云&#xff0c;对每一个scan进行edge特征点的提取&#xff0c;因为提取的特征点因为噪声等原因导致特征点不鲁棒&#xff0c;所以又对特征点通过聚类的方法进行了特征增强&#xff0c;对增加后的特征投影到平面上&#xff0c;分别…

无线通信模块|wifi模块、BLE蓝牙模块、wifi蓝牙二合一模块科普介绍

物联网技术能够帮助智能家居环境中的门厅场景、客厅场景、厨房场景、卧室场景以及阳台场景的智能化管理。现如今智能家居和智能应用之间的配合离不开物联网模块的帮助。没有WiFi模块智能家居就不能很好的工作&#xff0c;甚至不能很好的进行数据的交换。现阶段的物联网智能家居…

程序员必须知道的加密、解密和签名算法

1. 对称加密 对称加密&#xff0c;加密和解密使用相同的秘钥&#xff0c;加密速度快、效率高。常见的有 DES&#xff08;淘汰&#xff09;、3DES&#xff08;淘汰&#xff09;、AES&#xff08;用于替代 DES&#xff0c;是目前常用的&#xff09;等。 加密解密 1.1. DES&#…

建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式

思考&#xff1a; 为什么需要建造者模式 与工厂模式有何区别&#xff1f; 为什么需要建造者模式&#xff1f; 平时常常使用new关键字来创建对象&#xff0c;什么时候new对象时候不适用了呢&#xff1f;可能是创建对象时候可能是构造函数中传入太多的内容吧。 下面通过一个例子…

基于Java电脑硬件库存管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Linux下从CPU/内存/IO三个方面来分析系统性能

在实际生产环境中是否遇到如下问题&#xff1f; 系统平均负载过高。 CPU使用率过高。 硬盘利用率已经饱和&#xff0c;IO存在瓶颈。 首先明确一下进程的常见6种状态 R运行状态&#xff08;running&#xff09;&#xff1a;并不意味着进程一定在运行中&#xff0c;它表明进程要…

钉钉聊天对话框和截图经常发生白屏

环境&#xff1a; 7.0.30-rel6019102 Win10专业版 L盾加密环境 问题描述&#xff1a; 钉钉聊天对话框和截图经常发生白屏 解决方案&#xff1a; 1.【电脑端钉钉】- 左上角【头像】-【设置】-【高级】- 下拉【网络检测】- 点击【开始检测】 如果变红说明网络有问题&#x…

redis安装后启动报redis-server.exe redis.windows.conf

文章目录 1. 报错的内容2. 解决方法&#xff1a;&#xff08;亲测有效&#xff09; 1. 报错的内容 redis安装后启动报redis-server.exe redis.windows.conf 完整报错如下&#xff1a; 2. 解决方法&#xff1a;&#xff08;亲测有效&#xff09; 先使用命令切换到redis安装目…

Redis常用命令操作

#linux是redis-cli #普通环境 redis-cli.exe -h host -p port -a password #集群环境&#xff0c;否则报&#xff1a;(error) MOVED 6918 127.0.0.1:6381 redis-cli.exe -c -h host -p port -a password#参数说明 #host&#xff1a;远程redis服务器host #port&#xff1a;远程r…