【算法系列篇】递归、搜索与回溯(一)

在这里插入图片描述

文章目录

  • 什么是递归、搜索与回溯算法
  • 1. 汉诺塔
    • 1.1 题目要求
    • 1.2 做题思路
    • 1.3 代码实现
  • 2. 合并两个有序链表
    • 2.1 题目要求
    • 2.2 做题思路
    • 2.3 代码实现
  • 3. 反转链表
    • 3.2 题目要求
    • 3.2 做题思路
    • 3.3 代码实现

什么是递归、搜索与回溯算法

递归算法是一种通过重复将问题分解为同类的子问题而解决问题的方法。递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过调用自身来进行递归。

搜索算法是利用计算机的高性能来有目的地穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。主要包括枚举算法、深度优先搜索、广度优先搜索、A*算法、回溯算法、蒙特卡洛树搜索和散列函数等算法。

回溯算法实际上是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

那么,为什么会将这三个算法放在一起呢?其实搜索算法和回溯算法本质上都是递归算法,只是搜索决定了递归的方式,而回溯则是在递归搜索的时候,在函数返回值的时候将改变的量给还原回来。

在本系列算法中,我将为大家一步一步、一个阶段一个阶段的为大家分享涉及到相关知识的题目。

1. 汉诺塔

https://leetcode.cn/problems/hanota-lcci/

1.1 题目要求

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

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

你需要原地修改栈。

示例1:

输入:A = [2, 1, 0], B = [], C = []
输出:C = [2, 1, 0]

示例2:

输入:A = [1, 0], B = [], C = []
输出:C = [1, 0]

提示:

A中盘子的数目不大于14个。
class Solution {public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {}
}

1.2 做题思路

这是⼀道递归⽅法的经典题⽬,我们可以先从最简单的情况考虑:

  • 假设 n = 1,只有⼀个盘⼦,很简单,直接把它从 A 中拿出来,移到 C 上;
  • 如果 n = 2 呢?这时候我们就要借助 B 了,因为⼩盘⼦必须时刻都在⼤盘⼦上⾯,共需要 3 步(为
    了⽅便叙述,记 A 中的盘⼦从上到下为 1 号,2 号):
    a. 1 号盘⼦放到 B 上;
    b. 2 号盘⼦放到 C 上;
    c. 1 号盘⼦放到 C 上。
    ⾄此,C 中的盘⼦从上到下为 1 号,2 号
  • 如果 n > 2 呢?这是我们需要⽤到 n = 2 时的策略,将 A 上⾯的两个盘⼦挪到 B 上,再将最⼤的盘
    ⼦挪到 C 上,最后将 B 上的⼩盘⼦挪到 C 上就完成了所有步骤。例如 n = 3 时如下图
    在这里插入图片描述

然后再假设我们要将 4 个圆盘从 A 柱上借助 B 柱给移动到 C 柱上。
在这里插入图片描述

先将上面的三个盘子从 A 柱借助 C 柱移动到 B 柱。
在这里插入图片描述

在这里插入图片描述

然后将 A 柱剩下的那个最大的盘子移动到 C 柱之后,再将 B 柱上的 3 个盘子通过 A 柱移动到 C 柱上。

在这里插入图片描述

汉诺塔的规则是一次只能移动一个盘子并且需要保证较小的盘子在较大盘子的上面。那么要想将这 4 个盘子从 A 柱上借助 B 柱移动到 C 柱的话,首先就需要想办法将 A 柱上的 3 个盘子给移动到 B 柱上,然后将剩下的最大的盘子移动到 C 柱上,然后再将 B 柱上的 3 个盘子借助 A 柱移动到 C 柱上,最终就能达到我们的目标。而将 3 个盘子从 A 柱借助 B 柱移动到 C 柱就可以借助最上面的那个例子实现。

我们只看上面三个盘子的起点和终点就可以发现这三个盘子是从 A 柱借助 B 柱移动到 C 柱上,跟上面 3 个盘子的情况是一样的。当盘子为 5 个的时候,也是先将上面的 4 个盘子借助 C 柱移动到 B 柱,然后将剩下的一个盘子移动到 C 柱,最后再将 B 柱上的 4 个盘子借助 A 柱给移动到 C 柱上。上面 4 个盘子的移动路径也是
A柱 -> C柱,符合将大事化小的思想,所以我们就可以将汉诺塔的问题看成是一个递归。

并且通过上面的例子,我们可以总结出:当 A 柱只有一个盘子的时候,可以直接将这个盘子从 A 柱移动到 C 柱上,其他情况则遵循下面的步骤。

  1. 将 n-1 个盘子从 A 柱借助 C 柱移动到 B 柱。
  2. 将 A 柱上剩余的 1 个盘子直接移动到 C 柱
  3. 将 B 柱上的 n-1 个盘子借助 A 柱移动到 C 柱

那么知道了思路是什么,那么代码该怎么写呢?递归代码该如何写才不会出现栈溢出/死递归的问题呢?

其实我们要有这样的一种思想:以宏观的角度来解决微观问题,这是什么意思呢?就是当我想要将 n-1 个盘子从 A 柱借助 C 柱移动到 B 柱的时候,我们只需要将这件事交给函数,并且相信这个函数一定能够帮助我们解决这个问题。

1.3 代码实现

class Solution {public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {dfs(A, B, C, A.size()); //将n个盘子从A柱借助B柱移动到C柱}private void dfs(List<Integer> A, List<Integer> B, List<Integer> C, int n) {//当A柱上只有一个盘子的时候,则只需要将这个盘子移动到C柱上if (n == 1) {C.add(A.remove(A.size() - 1));return;}dfs(A, C, B, n - 1); //将n-1个盘子从A柱上借助C柱移动到B柱C.add(A.remove(A.size() - 1)); //将A柱剩下的那个盘子移动到C柱dfs(B, A, C, n - 1); //将B柱上的n-1个盘子借助A柱移动到C柱}
}

在这里插入图片描述

如果大家对这个代码有疑问的话,大家可以详细的画出这个代码的递归函数过程。

其实想要做好递归的题目,关键就在于我们能够总结出规律,只要发现这个提供通过一个函数就可以解决,那么基本就可以做出这个决定:使用递归来解决,并且在做递归类的题目的时候,我建议大家不要去细究它是如何递归的,我们只需要相信这个函数一定能帮助我们解决问题就可以了。

2. 合并两个有序链表

https://leetcode.cn/problems/merge-two-sorted-lists/

2.1 题目要求

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

示例 1:

在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeTwoLists(ListNode list1, ListNode list2) {}
}

2.2 做题思路

这道题目,相比大家之前肯定做过了吧,之前的做法就是用两个指针分别遍历这两个链表,一开始两个指针 cur1 和 cur2 都指向链表的头结点,如果 cur1 指向的节点的值小于 cur2 指针指向的节点的值,那么就将 cur1 节点的下一个节点指向 cur2 ,然后 cur2 向后移动一位,一次操作就能达到合并两个有序链表的操作了。

那么为什么在写递归算法的时候,还会将这个题目给搬出来呢?这是因为这道题目同样可以使用递归的方式来解决,为什么这么说呢?合并链表的操作也是跟上面的思路相同,比较两个指针指向的节点的大小,然后根据这两个节点的大小来讲这两个节点进行排序,也就是说两个节点排序可以使用同一个函数来实现,并且合并两个整个链表,实际上也是这两个链表中的两个节点在比较。这就适合递归将大事化小,可以使用同一个函数解决的思路。

那么,如何使用递归解决这个问题呢?在一个函数中我们只解决两个节点的比较,如果 cur1 指针指向的节点的值小于 cur2 指向的节点的值的话,那就需要 cur2 指向的节点与 cur1.next 之后的节点进行比较,并且将比较的结果给接到 cur1 节点的后面。

在这里插入图片描述
在这里插入图片描述

2.3 代码实现

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode mergeTwoLists(ListNode l1, ListNode l2) {//当l1链表或者l2链表为空的时候可以直接返回if (l1 == null) return l2;if (l2 == null) return l1;if (l1.val < l2.val) {l1.next = mergeTwoLists(l1.next, l2);return l1;}else {l2.next = mergeTwoLists(l1, l2.next);return l2;}}
}

在这里插入图片描述

3. 反转链表

https://leetcode.cn/problems/reverse-linked-list/

3.2 题目要求

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

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:
在这里插入图片描述

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

链表中节点的数目范围是 [0, 5000]
-5000 <= Node.val <= 5000
/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseList(ListNode head) {}
}

3.2 做题思路

反转整个链表其实还是两两节点进行反转,并且反转的过程是一样的,所以就可以使用我们的递归来解决这个问题。

看到这个题目我们可以想到的方法就是从链表的第一个节点开始,将该节点的指向指向前一个节点,那么就可以将这个链表分为两个部分,一个就是第一个节点,另一部分就是后n-1个节点,我们先将后面n-1个节点进行反转,然后将反转的链表的头结点返回,最后在将这个第一个节点接在反转后的链表的尾部,就得到了最终的反转链表。

在这里插入图片描述
在这里插入图片描述

3.3 代码实现

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseList(ListNode head) {if (head == null || head.next == null) return head;ListNode newHead = reverseList(head.next); //先反转后面n-1个节点//将head节点加在后面n-1个节点反转后的链表的后面head.next.next = head;head.next = null;return newHead;}
}

在这里插入图片描述

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

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

相关文章

【一天一坑系列】Mysql中INSERT IGNORE INTO插入时去重无效,插入了重复数据

1、问题描述 今天刚好有一个需求&#xff0c;需要插入数据时去重。所以第一下就想到了insert ignore into方法&#xff0c;但是使用过程中&#xff0c;发现虽然设置了唯一键&#xff0c;但插入时还是出现了重复插入的情况。 具体信息如下&#xff0c;表的约束设置如下&…

从Excel中找sheet

pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 …

win11安装(未完待续)

学习补丁 test.bat 运行后需要重启 slmgr /ipk W269N-WFGWX-YVC9B-4J6C9-T83GX slmgr /skms kms.03k.org slmgr /ato 文件扩展名 主题 性能设置 开始按钮靠左 任务栏对齐方式-靠左 必备软件 f.lux redshift 360管家 驱动精灵 edge c*lash&#xff08;v2*ray不支持w…

显示屏 LM3033(ST7920主控)

简介 这是常用的单色液晶 LCD 显示屏。 型号为 LM3033DFW&#xff08;深圳拓普微&#xff09;5V 单电源供电&#xff08;3.3V不可以&#xff0c;对比度会降低到看不清&#xff09;支持并口&#xff08;8080时序&#xff09;和串行通讯&#xff08;SPI&#xff09;带字库 框图…

git stash 对当前分支修改的内容进行暂存

我们在开发的时候往往会遇到这种情况, 在一个分支开发,写了不少内容,但是突然来了一个紧急的需求需要切换分支,去做这个需求,但是当前的分支又因为没有开发完成,不想形成一条无效的commit记录,这时我们就到暂存上场了 git stash 暂存 // 切分支之前 对当前分支修改的内容进行暂…

制作一个RISC-V的操作系统五-RISC-V汇编语言编程二

文章目录 RISC-V汇编指令操作对象RISC-V汇编指令编码格式小端序的概念RISC-V汇编指令分类RISC-V汇编伪指令 RISC-V汇编指令操作对象 RV32I&#xff1a;RISC-V32位机器整数指令集 指令集分非特权指令集和特权指令集 XLEN&#xff1a;变量代表当前机器的字长&#xff08;32位 64…

ModbusRTU\TCP消息帧解析(C#实现报文发送与解析)

目录 知识点常用链接一、Modbus1.ModbusRTU消息帧解析2.主站poll、从站slave通讯仿真-modbusRTU1.功能码01读线圈状态2.功能码03读保持寄存器报文解析&#xff08;寄存器存整型&#xff09;报文解析&#xff08;寄存器存float&#xff09; 3.C#模拟主站Poll&#xff08;ModbusR…

Go 语言中的反射机制

欢迎大家到我的博客浏览&#xff0c;更好的阅读体验请点击 反射 | YinKais Blog 反射在大多数的应用和服务中并不常见&#xff0c;但是很多框架都依赖 Go 语言的反射机制简化代码。<!--more-->因为 Go 语言的语法元素很少、设计简单&#xff0c;所以它没有特别强的表达能…

Mysql的所有数据类型和它们的区别

一、数值类型 1. 普通整数数值类型 以下数据类型只能用以保存整数 整数数值类型类型存储大小&#xff08;字节&#xff09;有符号的取值范围&#xff08;允许存在负数&#xff09;无符号的取值范围TINYINT1-128 ~ 1270 ~ 255SMALLINT2- 327678 ~ 327670 ~ 65535MEDIUMINT3- 8…

华清远见嵌入式学习——C++——作业6

作业要求&#xff1a; 代码&#xff1a; #include <iostream>using namespace std;class Animal { public:virtual void perform() 0;};class Lion:public Animal { private:string foods;string feature; public:Lion(){}Lion(string foods,string feature):foods(foo…

【Spring Boot】如何在IntelliJ IDEA中由同一份spring boot源码运行多个不同端口的实例

我们需要使用一个服务有多个实例的测试场景&#xff0c;那么我们就需要在IntelliJ IDEA中通过不同的端口运行不同的实例&#xff0c;并且运行时的源代码是一样的&#xff0c;那么我们可以在IntelliJ IDEA这样操作&#xff0c;接下来以UserApplication服务为例&#xff1a; 复制…

使用Java API操作HDFS

文章目录 一、了解HDFS Java API&#xff08;一&#xff09;HDFS Java API概述1、配置&#xff08;Configuration&#xff09;2、文件系统&#xff08;FileSystem&#xff09;3、路径&#xff08;Path&#xff09;4、输入输出流&#xff08;FSDataInputStream 和 FSDataOutputS…

codeforces 题目 Powers Of Two

目录 题目&#xff1a; 题目描述&#xff1a; 思路&#xff1a; AC代码&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 给你两个整数 n 和 k 问是否能找到 k 个2的幂&#xff0c;使其总和为 n 若能&#xff0c;则输出这 k 个 2的幂&#xff1b;若不能&#xff0c;…

预览控制;预见控制;预测控制;预观控制(preview control)

预演控制&#xff08;preview control&#xff09;作为一种新兴的控制方法&#xff0c;首次在轮式车辆中被提出。 参考文献&#xff1a; https://www.sciencedirect.com/science/article/pii/S0016003219300390https://www.sciencedirect.com/science/article/pii/S0016003219…

Ardupilot开源飞控之VTOL之旅:配件试装

Ardupilot开源飞控之VTOL之旅&#xff1a;配件试装 1. 源由2. 分析2.1 【修改使用】FC & PDB & GPS打印件2.2 【直接使用】VTX & CRSF打印件 3. 试装3.1 【结构】问题1&#xff1a;GPS座子尺寸非常紧凑&#xff0c;需要用力压入卡座内。3.2 【结构】问题2&#xff…

实验报告-实验四(时序系统实验)

软件模拟电路图 说明 SW&#xff1a;开关&#xff0c;共六个Q1~Q3&#xff1a;输出Y0~Y3&#xff1a;输出 74LS194 首先&#xff0c;要给S1和S0高电位&#xff0c;将A~D的数据存入寄存器中&#xff08;如果开始没有存入数据&#xff0c;那么就是0000在里面移位&#xff0c;不…

智慧小区园区如何布局网络对讲系统

智慧小区园区如何布局网络对讲系统 随着小区住宅的不断更新发展&#xff0c;小区的管理人员也对小区内部的通讯也有了新的要求&#xff0c;要求在工作区域无盲区、语音通讯清晰&#xff0c;小区的安保后勤都能够随时在小区的地下室和室外工作区域、任何时间进行通信。提高小区…

Python 云服务器应用,Https,定时重启

Python 云服务器应用,Https,定时重启 环境搭建Python模块模块导入生成Flask实例GET处理启动服务器打开网页验证 GET接入证书 支持https申请证书下载证书保留 xxx.crt 和 xxx.key文件就可以了 copy到python项目目录ssl_context 配置 宝塔面板操作在www目录下新建python工作目录在…

CRM立项正当时|走过复杂多变的2023年,明年如何锚定确定性增长?

正值年末&#xff0c;又到复盘今年、规划明年的重要节点。 2023年&#xff0c;黑天鹅和新风口轮番登场&#xff0c;当不确定成常态&#xff0c;环境愈发错综复杂&#xff0c;企业家们如何深谋远虑&#xff0c;带领企业实现可持续、高质量发展&#xff1f;这里提供三个思考视角…

LoadRunner12.55的简介与安装

提示&#xff1a;https://mp.weixin.qq.com/s/iK-fh0VP7v8mNSDNxjkBow 文章目录 LoadRunner的简介与安装loadrunner概述loadrunner的下载与安装 LoadRunner的使用启用VuGen LoadRunner的简介与安装 LoadRunner官网&#xff1a;https://www.microfocus.com/zh-cn/products/load…