汉诺塔 --- 递归回溯算法练习一

news2024/11/25 13:36:28

目录

1. 什么叫汉诺塔

2. 分析算法原理

2.1. 当盘子的数量为1

 2.2. 当盘子的数量为2

2.3. 当盘子的数量为3时

3. 编写代码

3.1. 挖掘重复子问题

3.2. 只关心某一个子问题如何处理

3.3. 递归的结束条件

3.4. 代码的编写

4. 递归展开图分析


1. 什么叫汉诺塔

力扣上的原题链接如下:

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

什么叫汉诺塔呢?

汉诺塔是一种经典的数学智力游戏,它起源于印度。游戏中有三个竖立的柱子,通常称为"A"、“B”、“C”。最初,所有的圆盘按照从大到小的顺序叠放在"A"柱子上,最大的圆盘在底部,最小的圆盘在顶部。游戏的目标是将所有的圆盘从"A"柱子移动到"C"柱子上,但在移动过程中要遵守以下规则:

  1. 一次只能移动一个圆盘;
  2. 每次移动必须将一个圆盘从柱子的顶部移到另一个柱子的顶部;
  3. 移动过程中,大圆盘不能放在小圆盘的上面。

通过按照规则逐个移动圆盘,最后把所有圆盘都移动到"C"柱子上就完成了游戏。汉诺塔问题被广泛用作数学问题和编程算法的练习。它展示了递归算法的应用,因为解决汉诺塔问题的常用方法就是利用递归。

2. 分析算法原理

2.1. 当盘子的数量为1

初始情况如下:

此时只需要将A柱子的盘子直接移动到C柱子上即可。 

 2.2. 当盘子的数量为2

初始情况如下:

我们的目的是,将这两个盘子从A柱子移动到C住柱子。而要想将a盘子移动到C柱子上,那么首先要将b移动到B柱子上。

此时我们可以分为三个过程:

step 1:将b盘子以C柱子为辅助,移动到B柱子。

step 2:将a盘子直接移动到C柱子。

step 3:将b盘子以A柱子为辅助,移动到C柱子。

step 1:

将b盘子以C柱子为辅助,移动到B柱子

 

step 2:

将a盘子直接移动到C柱子

 

step 3:

将b盘子以A柱子为辅助,移动到C柱子

此时经过上面的三个过程就达到了我们的目的。

2.3. 当盘子的数量为3时

我们的目的是,将这三个盘子从A柱子移动到C住柱子。而要想将a盘子移动到C柱子上,那么首先要将a上面的柱子(把b和c看作为一个整体)移动到B柱子上。此时将b和c移动到B柱子上不就是当盘子数量为2吗,因此此时我们依旧可以分为三个过程:

step 1:将b和c盘子以C柱子为辅助,移动到B柱子。

step 2:将a盘子直接移动到C柱子。

step 3:将b和c盘子以A柱子为辅助,移动到C柱子。

step 1:将b和c盘子以C柱子为辅助,移动到B柱子

step 2: 将a盘子直接移动到C柱子。

step 3: 将b和c盘子以A柱子为辅助,移动到C柱子

以此类推,我们发现,对于一个大问题,是可以被分为若干个相同类型的子问题,而这就是能用递归的原因。

那么我们的总结就是:

递归结束条件: 当盘子的数量 == 1时,直接处理; 

递归中间过程: 当盘子的数量 >= 2时,我们分三个步骤处理; 

我们将最下面的盘子称之为 Last; Last上面的所有盘子称之为 All

step 1: 将A柱子 "Last" 的 "All"  借助C柱子 移动到 B柱子。

step 2: 将A柱子 "Last" 直接移动到 C柱子上。

step 3: 将B柱子 "ALL" 借助 A柱子 移动到 C柱子上。

3. 编写代码

既然可以用递归处理,那么如何编写递归代码呢?我们可以分为三个步骤。

3.1. 挖掘重复子问题

这个步骤决定了递归的函数头,将问题抽象为函数头

汉诺塔的重复子问题:

将A柱子上的一堆盘子,以B柱子为辅助,移动到C柱子。

// 函数头如下:
// A、B、C 分别代表三个柱子
// n代表你要将几个盘子从A柱子,以B盘子为辅助,移动到C柱子
void _hanota(A,B,C,n);

3.2. 只关心某一个子问题如何处理

这个步骤决定了递归的函数体。这一步只需要关注某一个子问题如何处理的,经过我们的分析算法原理,我们得知了任何一个子问题可以被分为三个过程,如下:

我们将最下面的盘子称之为 LastLast上面的所有盘子称之为 All

step 1: 将A柱子 "Last" 的 "All"  借助C柱子 移动到 B柱子。

step 2: 将A柱子 "Last" 直接移动到 C柱子上。

step 3: 将B柱子的 "ALL" 借助 A柱子 移动到 C柱子上。

//step 1: 将A柱子 "Last" 的 "All"  借助C柱子 移动到 B柱子。
void _hanota(A,C,B,n-1);
//step 2: 将A柱子 "Last" 直接移动到 C柱子上。
A.back() --->  C
//step 3: 将B柱子上的 "ALL" 借助 A柱子 移动到 C柱子上。
void _hanota(B,A,C,n-1);

3.3. 递归的结束条件

这个步骤决定了递归的出口,如何判定呢? 当一个问题不可以在被分为相同类型的子问题时此时就是递归的结束条件

经过我们前面的分析,当盘子的数量 == 1时,直接处理,此时就是递归的结束条件。

if(n == 1)
    return ;

3.4. 代码的编写

有了上面的分析,代码就非常简单了:

class Solution {
public:

    void _hanota(vector<int>& A, vector<int>& B, vector<int>& C,size_t n)
    {
        // 递归出口
        if(n == 1)
        {
            C.push_back(A.back());
            A.pop_back();
            return ;
        }
        // step 1:
        _hanota(A,C,B,n-1);
        // step 2:
        C.push_back(A.back());
        A.pop_back();
        // step 3:
        _hanota(B,A,C,n-1);
    }

    void hanota(vector<int>& A, vector<int>& B, vector<int>& C) {
        _hanota(A,B,C,A.size());
    }
};

4. 递归展开图分析

为了更好地理解上面的代码,我们试画一下当N == 3时的递归展开图。同样,为了更好地画递归图,我们将递归出口的逻辑以  ’#‘ 表示,step 1、step 2、step 3、 分别以 (1)、(2)、(3)表示。

经过我们上面的函数的左边的递归展开图,我们应该可以了解它的过程了,当我们以宏观角度看,认为把A柱子上的b和c这两个盘子经由C柱子辅助移动到B柱子,是一个步骤;但是递归展开后,我们发现,每次当函数的盘子数量1时,才会移动盘子(而且移动的最上面的盘子)。所以我们的代码中移动的是A.back(),最后一个元素,而不是A[0]。

至此,我们的汉诺塔就此结束。

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

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

相关文章

Go并发编程(上)

目录 一、go语言当中的协程 二、MPG模型介绍 三、Goroutine 的使用 3.1 协程的开启 3.2 优雅地等待子协程结束 四、捕获子协程的panic 五、管道Channel 5.1、认识管道 5.2、Channel的遍历和关闭 5.3 、用管道实现生产者消费者模型 5.4、Channel一些使用细节和注意事…

揭秘!2024年热门的图标设计工具

在这个瞬息万变的世界里&#xff0c;设计师们对创新和实用的工具的渴望日益热切。我们需要时刻紧跟潮流&#xff0c;发掘和尝试最新&#xff0c;最有价值的图标设计工具&#xff0c;才能在剧烈的市场竞争中引人注目。以下是我们细心挑选的2024年图标设计工具的热门推荐&#xf…

CentOS 7 双网卡绑定热备 —— 筑梦之路

为什么需要&#xff1f; 1. 增强网络的可靠性 2. 保障服务的可持续性 3. 降低网卡故障带来的不良影响 有哪些模式&#xff1f; 模式0&#xff1a;轮询策略&#xff08;round robin&#xff09;&#xff0c;mode0&#xff0c;优点&#xff1a;流量提高一倍缺点&#xff1a;需要接…

《QT从基础到进阶·十五》用鼠标绘制矩形(QGraphicsView、QPainter、QGraphicsRectItem)

以下是鼠标绘制矩形最全的一种用法&#xff0c;完整源码将会放在最后面。 QT版本&#xff1a;5.15.2 VS版本&#xff1a;2019 1、在界面加载一张图片 界面的搭建选用QGraphicsView&#xff0c;自定义类GraphicsView继承QGraphicsView&#xff0c;在主程序中点击按钮打开 图片&…

opencv4笔记

图像二值化 全局法Threshold 大津法 大津法OSTU阈值类型——适用于双峰直方图 OTSU算法也称最大类间差法&#xff0c;由大津于1979年提出&#xff0c;被认为是图像分割中阈值选取的最佳算法&#xff0c;计算简单&#xff0c;不受图像亮度和对比度的影响&#xff0c;它是按图…

浅谈泛在电力物联网在智能配电系统应用

贾丽丽 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;在社会经济和科学技术不断发展中&#xff0c;配电网实现了角色转变&#xff0c;传统的单向供电服务形式已经被双向能流服务形式取代&#xff0c;社会多样化的用电需求也得以有效满足。随着物联网技术的发展&am…

c#数据类型

常量 /*常量是固定的量&#xff0c;在运行过程中不可以改变的量 const 来修饰不能改变的量*/ // public private protected internal 是访问修饰符using System.Security.Cryptography.X509Certificates;namespace ConsoleApp1 {internal class Program{public const int a 1…

科技云报道:数智化升级,如何跨越数字世界与实体产业的鸿沟?

科技云报道原创。 数智化是当下商业环境下最大的确定性。 2022年&#xff0c;中国数字经济规模达50.2万亿元&#xff0c;占国内生产总值比重提升至41.5%&#xff0c;数字经济成为推动经济发展的重要引擎。从小型创业公司到跨国巨头&#xff0c;数字化转型在企业发展历程中彰显…

javaSE学习笔记(二)数组,类,对象,成员变量,匿名对象,构造方法,static,final,封装,继承,多态

目录 三、面向对象 1.概述 面向过程与面向对象 面向对象编程特点 面向对象三个基本特征 2.数组 数组定义格式 数组的初始化 动态初始化 静态初始化 数组的内存分配 Java中的内存分配 数组的内存分配 数组的角标 数组的基本操作 二维数组&#xff08;实际开发几乎…

maven 下载和安装

点击进入Maven下载网址 Maven – Download Apache Maven Maven详细下载列表 Index of /dist/maven/maven-3 本地仓库配置文件 配置环境变量 idea编辑器配置 maven Javajdk配置 字节码版本是否11

yolov5 通过视频进行目标检测

打开yolov5-master文件夹&#xff0c;可以看到一个名为data的文件夹&#xff0c;在data中创建一个新的文件夹&#xff0c;命名为videos。 打开yolov5-master中的detect.py可以看到一行代码&#xff08;大概在245行左右&#xff09;为 parser.add_argument(--source, typestr,…

POI.5.2.4常用操作-数据导入导出常规操作

1、APACHE POI简介 Apache POI 简介是用Java编写的免费开源的跨平台的 Java API&#xff0c;Apache POI提供API给Java程式对Microsoft Office&#xff08;Excel、WORD、PowerPoint、Visio等&#xff09;格式档案读和写的功能。 1.1、其他操作Excel工具 Java Excel是一开放源码…

类直径树上贪心

http://cplusoj.com/d/senior/p/SS231109C 场上想到枚举点&#xff0c;然后最大值为高&#xff0c;然后可以求最大值。但是感觉计数会重 计数其实不会重&#xff0c;如图中&#xff0c;红色线段显然比蓝色线段优 所以我们枚举3叉点时没错的 #include<bits/stdc.h> usin…

Terminator终端

Terminator终端 terminator快捷键 sudo apt install terminator使用ctrlaltt开启终端 terminator快捷键 E O W 都是ctrl shift结合 alt方向键&#xff0c;为切换焦点 e分屏失效的解决方案&#xff08;原因是快捷键占有问题&#xff09; ibus-setup将表情符号注释就行了 …

ARMday2(环境创建+工程配置+创建文件+单步调试)

目录 一、汇编环境的创建 二、为工程配置链接脚本&#xff08;map.lds&#xff09; 三、为工程创建汇编文件 start.s 编程调试 接下来我们需要建立一个 start.s 汇编文件添加到我们的工程中去 四、对汇编代码进行单步调试&#xff08;仿真&#xff09; 五、汇编工程的编译 …

遍历List集合和Map进行修改和删除报java.util.ConcurrentModificationException错误详解

一、异常产生 当我们使用foreach迭代一个ArrayList或者HashMap时&#xff0c;如果尝试对集合做一些修改操作&#xff08;例如删除元素或新增&#xff09;&#xff0c;可能会抛出java.util.ConcurrentModificationException的异常。 javapublic static void main(String[] args)…

语音控制:基于ESP8266的DIY助手

随着智能家居的兴起&#xff0c;语音控制成为越来越受欢迎的方式。在本篇文章中&#xff0c;我们将向您介绍如何使用ESP8266构建一个基于语音控制的DIY助手。 第一步&#xff1a;硬件准备 在开始之前&#xff0c;您需要准备一些基础硬件&#xff0c;包括ESP8266模块、麦克风模…

【C++】C++中的IO流

文章目录 一、C语言的输入与输出二、什么是流三、CIO流1.C标准IO流2.C文件IO流 四、stringstream的简单介绍 一、C语言的输入与输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf(): scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量对应的…

数据结构-图的存储

邻接矩阵法 #define MaxVertexNum 100 //顶点数目的最大值 typedef struct{char Vex[MaxVertexNum]; //顶点表int Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵&#xff0c;边表int vexnum,arcnum; //图的当前顶点数和边数 }MGraph;无向图 第i个顶点的度第i行&#xff08;或第…

深入理解JVM虚拟机第二十二篇:详解JVM当中与操作数栈相关的字节码指令

大神链接&#xff1a;作者有幸结识技术大神孙哥为好友&#xff0c;获益匪浅。现在把孙哥视频分享给大家。 孙哥链接&#xff1a;孙哥个人主页 作者简介&#xff1a;一个颜值99分&#xff0c;只比孙哥差一点的程序员 本专栏简介&#xff1a;话不多说&#xff0c;让我们一起干翻J…