暴力递归汉诺塔问题

暴力递归

  1. 将问题转化为规模缩小了的同类问题的子问题。
  2. 有明确的不需要继续递归的条件(base case)
  3. 有当得到了子问题的结果之后的决策过程
  4. 不记录每一个子问题的解

暴力递归的要点大致可以分为以上四条,但是总结起来就是一句话:不断的尝试,所尝即所得

下面用著名的汉诺塔问题来进行带入讲解。

汉诺塔
简单的阐述一下汉诺塔问题:有3根柱子,依次从左到右排列分别是left、mid、right,假设此时左柱上有1、2、3,3个有小到大的圆盘,每次只能挪动一个圆盘,并且小圆盘要永远在大圆盘之上。几步可以将圆盘从left移到right。
在这里插入图片描述
结论:3个圆盘共需要7步

  • 1圆盘left -> right
  • 2圆盘left -> mid
  • 1圆盘right ->mid
  • 3圆盘left -> right
  • 1圆盘mid -> left
  • 2圆盘mid->right
  • 1圆盘left -> right

如何用暴力递归解决
上面的7小步可以统一总结为3大步,分别是:

  1. 上面的2个挪到中间
  2. 最下层最大的挪到右边
  3. 中间的两个挪到右边。

再来确定base case(当什么时候我可以随意挪动):是当圆盘数为1时,如果此时left柱上只剩1个圆盘,那一步就可以直接到right柱。
OK,我们来完善下这部分的代码:

hanoi

  public static void hanoi1(int N) {
        //主过程从最左挪到最右
        leftToRight(N);
    }


  public static void leftToRight(int N) {
       //base case
       //当我只剩1层圆盘时,就可以直接从left -> right
       if (N == 1) {
          System.out.println("Move 1 from left to right");
           return;
       }
       //不止1个圆盘
       //想从 left —> right
       //第一大步: 先将N - 1位置的圆盘,left -> mid
       leftToMid(N - 1);
       //第二大步: 上面挪完,将自己 left -> right
       System.out.println("Move " + N + " from left to right");
       //第三大步 将其他圆盘 mid -> right
       midToRight(N - 1);
  }

上面已经将整体分为了三大步,并且将主流程和每一步对应的操作流程给了出来,下一步,我们再来深入到每一步的流程。

leftToMid
如果我想将圆盘从left -> mid。是不是依然可以分为3大步:

  1. 将圆盘从left -> right
  2. base case 剩我自己时,可以随意挪动
  3. 将其余圆盘从right -> mid。

我们再来完善这部分的细节代码:

public static void leftToMid(int N) {
        if (N == 1) {
            System.out.println("Move 1 from left to mid");
            return;
        }
        leftToRight(N - 1);
        System.out.println("Move " + N + " from left to mid");
        rightToMid(N - 1);
    }

其余主流程剩余的midToRight,是不是也同理。所以完整代码如下:

 public static void hanoi1(int N) {
        leftToRight(N);
    }

    public static void leftToRight(int N) {
        if (N == 1) {
            System.out.println("Move 1 from left to right");
            return;
        }
        leftToMid(N - 1);
        System.out.println("Move " + N + " from left to right");
        midToRight(N - 1);
    }

    public static void leftToMid(int N) {
        if (N == 1) {
            System.out.println("Move 1 from left to mid");
            return;
        }
        leftToRight(N - 1);
        System.out.println("Move " + N + " from left to mid");
        rightToMid(N - 1);
    }

    private static void midToRight(int N) {
        if (N == 1) {
            System.out.println("Move 1 from mid To right");
            return;
        }
        midToLeft(N - 1);
        System.out.println("Move " + N + " from mid to right");
        leftToRight(N - 1);
    }

    private static void midToLeft(int N) {
        if (N == 1) {
            System.out.println("Move 1 from mid to left");
            return;
        }
        midToRight(N - 1);
        System.out.println("Move " + N + " from mid to left");
        rightToLeft(N - 1);
    }

    private static void rightToLeft(int N) {
        if (N == 1) {
            System.out.println("Move 1 from right to left");
            return;
        }

        rightToMid(N - 1);
        System.out.println("Move " + N + " from right to left");
        midToLeft(N - 1);
    }

    private static void rightToMid(int N) {
        if (N == 1) {
            System.out.println("Move 1 from right to mid");
            return;
        }
        rightToLeft(N - 1);
        System.out.println("Move " + N + " from right to mid");
        leftToMid(N - 1);
    }

OK,汉诺塔的问题已经解决了,整体的思路就是先规划好整体的几大步并确定base case,在根据大的步骤完善细节。
但是不知道你们有没有发现,虽然解决了汉诺塔问题,但是代码太过冗余,left -> mid , left -> right , mid -> right , mid -> left,每一个都对应着两个方法。
那我们不妨将问题稍微抽象化一点,加一些参数,来简化这个方法。
抛去left,mid和right的概念,我们此时只有from to 和other,圆盘的目的是从from到to,我们依然分为3步来看:

  1. base case依然是当圆盘数为 1 时, 可以随意挪动。 这点无可厚非。
  2. 第一步: 想要圆盘数为 1 的话,还是要将N - 1的圆盘全部挪到其他圆盘上,但是此时,圆盘都在from,应该是从from -> to, to是谁? to应该是上面提到的other吧,只有将N - 1圆盘从 from 挪到了other上,最后一个圆盘才可以从from -> to。
  3. 第二步:此时,from上只剩一个圆盘,可以直接挪动从from -> to
  4. 第三步:是不是这一步再将N - 1挪到to上,整个汉诺塔问题就完成了。 怎么挪 ? 第一步将 N - 1从from挪到other,此时other是不是就是那个 from,to不变,from变成了other的概念。

将问题稍微抽象一些,并且增加了几个参数,此时再来看一看优化后的代码:
hanoi2

 public static void hanoi2(int N) {
        func(N, "left", "right", "mid");
    }

    public static void func(int N, String from, String to, String other) {
        if (N == 1) {
            System.out.println("Move 1 from " + from + " to " + to);
        } else {
            func(N - 1, from, other, to);
            System.out.println("Move " + N + " from " + from + " to " + to);
            func(N - 1, other, to, from);
        }
    }

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

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

相关文章

记录Taro巨坑,找不到sass类型定义文件

问题 taronutuisassts项目里tsconfig.json一直报红报错。 找不到“sass”的类型定义文件。 程序包含该文件是因为: 隐式类型库 “sass” 的入口点 其实正常人想的肯定是装上 types/sass试试。开始我试过了,没用。删了依赖重装也没用。后面在issue中找到答案了 解决…

SpringBoot + Vue 微人事(十二)

职位批量删除实现 编写后端接口 PositionController DeleteMapping("/")public RespBean deletePositionByIds(Integer[] ids){if(positionsService.deletePositionsByIds(ids)ids.length){return RespBean.ok("删除成功");}return RespBean.err("删…

案例-基于MVC和三层架构实现商品表的增删改查

文章目录 0. 项目介绍1. 环境准备2. 查看所有2.1 编写BrandMapper接口2.2 编写服务类,创建BrandService,用于调用该方法2.5 编写Servlet2.4 编写brand.jsp页面2.5 测试 3.添加3.1 编写BrandMapper接口 添加方法3.2 编写服务3.3 改写Brand.jsp页面&#x…

item_search_img-按图搜索淘宝商品(拍立淘)

一、接口参数说明: item_search_img-按图搜索淘宝商品(拍立淘),点击更多API调试,请移步注册API账号点击获取测试key和secret 公共参数 请求地址: https://api-gw.onebound.cn/taobao/item_search_img 名称类型必须描…

学Pyhton静不下来,看了一堆资料还是很迷茫是为什么

一、前言 最近发现,身边很多的小伙伴学Python都会遇到一个问题,就是资料也看了很多,也花了很多时间去学习但还是很迷茫,时间长了又发现之前学的知识点很多都忘了,都萌生出了想半路放弃的想法。 让我们看看蚂蚁金服的大…

Elasticsearch(十三)搜索---搜索匹配功能④--Constant Score查询、Function Score查询

一、前言 之前我们学习了布尔查询,知道了filter查询只在乎查询条件和文档的匹配程度,但不会根据匹配程度对文档进行打分,而对于must、should这两个布尔查询会对文档进行打分,那如果我想在查询的时候同时不去在乎文档的打分&#…

01_Redis单线程与多线程

01——Redis单线程与多线程 一、Redis是单线程还是多线程 在谈Redis的单线程或多线程时,需要根据版本来区分。 在redis 3.x之前,redis是单线程的从redis 4.x开始,redis引入多线程。处理客户端请求时,使用单线程;在异…

Android app 打包发布之build.gradle 配置

配置描述:在build.gradle(:app)文件中配置 包含以下几个部分: plugins:引入的工具android:主要配置都在这个里面dependencies:依赖android.applicationVariants.all:打包输出路径和名称 看android配置&a…

基于Spring Boot的餐厅订餐网站的设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频: 基于Spring Boot的餐厅订餐网站的设计与实现(Javaspring bootMySQL) 使用技术: 前端:html css javascript jQuery ajax thymeleaf 微信小程序 后端:Java springbo…

稳定扩散ControlNet v1.1 权威指南

ControlNet 是一种稳定扩散模型,可让你从参考图像中复制构图或人体姿势。 经验丰富的稳定扩散用户知道生成想要的确切成分有多难。图像有点随机。你所能做的就是玩数字游戏:生成大量图像并选择你喜欢的图片。 借助 ControlNet,稳定扩散用户…

0基础入门代码审计-2 Fortify初探

0x01 序言 目前又加入一位新童鞋了,最近将会再加入cs相关的专栏,都是以基础为主,毕竟太复杂的东西,能看懂的人太少。 0x02 准备工具 1、Fortify 2、需要审计的源码 0x03 Fortify的简单使用 1、 1、在开始菜单栏中找到Audit Wo…

3D WEB轻量化引擎HOOPS产品助力NAPA打造船舶设计软件平台

NAPA(Naval Architectural PAckage,船舶建筑包),来自芬兰的船舶设计软件供应商,致力于提供世界领先的船舶设计、安全及运营的解决方案和数据分析服务。NAPA拥有超过30年的船舶设计经验,年营业额超过2560万欧…

算法基础(1):排序和查找算法

1、排序算法 1.1、堆排序(大顶堆)-重点: 参考文章:堆排序1、堆排序二 前置知识: 大顶堆:完全二叉树,且父节点大于左右儿子,左右子树又是大顶堆,依赖数组来实现(vector)第一个节点的父节点&…

视频汇聚平台EasyCVR安防视频监控平台新增角色权限功能分配的具体操作步骤

视频集中存储/云存储/安防视频监控/视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等,以及支持厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等。 EasyCVR视频集中…

python连接PostgreSQL 数据库

执行如下命令安装 pip3 install psycopg2 python代码 Author: tkhywang 2810248865qq.com Date: 2023-08-21 11:42:17 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-08-21 11:51:56 FilePath: \PythonProject02\PostgreSQL 数据库.py Description: 这是默认设置…

想解锁禁用的iPhone?除了可以使用电脑之外,这里还有不需要电脑的方法!

多次输入错误的密码后,iPhone将显示“iPhone已禁用”。这种情况看起来很棘手,因为你现在不能用iPhone做任何事情。对于这种情况,我们提供了几种有效的方法来帮助你在最棘手的问题中解锁禁用的iPhone。你可以选择使用或不使用电脑来解锁禁用的iPhone。 一、为什么你的iPhone…

深度学习实战49-基于卷积神经网络和注意力机制的汽车品牌与型号分类识别的应用

大家好,我是微学AI,今天给大家介绍一下深度学习实战49-基于卷积神经网络和注意力机制的汽车品牌与型号分类识别的应用,该项目就像是一只智慧而敏锐的眼睛,专注地凝视着汽车世界。这个项目使用PyTorch作为强有力的工具,提供了一个深度学习的舞台,让我们能够设计和训练一个…

穿起“新架构”的舞鞋,跳一支金融数字化转型的华尔兹

华尔兹,是男女两位舞者,通过形体的控制,舞步技巧的发挥,完美配合呈现而出的一种舞蹈形式。华尔兹舞姿,如行云流水、潇洒自如、飘逸优美,素有“舞中皇后”的美称。 在跳华尔兹的时候,如果舞者双…

会议剪影|思腾合力受邀参加2023中国算力大会并作主题演讲

共享数字经济合作新机遇,携手开创算力产业新未来。8月18日-19日,由工业和信息化部、宁夏回族自治区人民政府主办的2023中国算力大会、第二届“西部数谷”算力产业大会在银川盛大启幕。思腾云计算总经理庞志刚、思腾合力市场总监徐莉受邀参会。 ▲思腾云计…

客户案例:高性能、大规模、高可靠的AIGC承载网络

客户是一家AIGC领域的公司,他们通过构建一套完整的内容生产系统,革新内容创作过程,让用户以更低成本完成内容创作。 客户网络需求汇总 RoCE的计算网络RoCE存储网络1.不少于600端口200G以太网接入端口,未来可扩容至至少1280端口1.…