汉诺塔与二进制、满二叉树的千丝万缕

news2024/9/24 1:25:38

汉诺塔(Tower of Hanoi)源于印度传说中,大梵天创造世界时造了三根金钢石柱子,其中一根柱子自底向上叠着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

汉诺塔递归算法

3阶汉诺塔移动步骤:

image

汉诺塔解法思路

一个规模为n的问题,可以拆成互相独立且与原问题形式相同的子问题的问题,可以采用递归方式解决子问题,然后将各个子问题的解合并得到原问题的解(分而治之思想)。

理解过程

如图,3阶的一共需要七步,

因为盘子3是最大的,所以所有盘子都可放在它上面,所以我们可以忽略盘子3,既是把“前三步”看做一个整体,完成2阶移动即可,移动目标是从A移动到B(伪C);

接着执行第四步,从A移到C,此时最大的盘就完成移动了,因为是最大,所以所有盘子都可以移到C,可以忽略盘子3,此时,后续的操作可以将3阶看成2阶来处理了;

“前三步”将盘子1和2,从A移到B了,托盘A和托盘B是相对来说的,此时的托盘B可以看做是托盘A,所以“后三步”2阶移动和普通的2阶移动步骤一样,移动目标是B(伪A)到C。

从上面分析可以知道,所有的移动都是从“A”移动到“C”,只是第一大步最后一大步是要交换位置,分别是C交换成B、B交换从A(看代码不太懂时,回来看这里)

当n=1时,只需托盘A直接移到托盘C(这是递归问题的出口); 当n>1时,需要借助另一托盘来移动,将n个圆盘由A移到C上可以分解为以下几个步骤: (1) 将A上的n-1个圆盘,借助C,从A移到B上; (2) 把A上第n个圆盘,直接从A移到C上; (3) 将B上的n-1个圆盘,借助A,从B移到C上。

递归方式实现的汉诺塔(Java版):

public class Hanoi {
    // 阶数
    private static int n = 4;
    //验证汉诺塔移动次数
    private static int sum=0;
    public static void main(String[] args) {
        System.out.println(String.format("%s层汉诺塔的移动顺序:", n));
        move(n, 'A','B','C');
        System.out.println("汉诺塔移动次数:"+sum);
    }

    /**
     * (n-1) A -> B
     *   n   A -> C
     * (n-1) B -> C
     * 
     * 结束条件为:当n=1 时, A -> C
     */
    public static void move(int n,char A, char B, char C) {
        if(n==1) {
            System.out.println(A + " -> " + C);
            sum++;
        }
        else {
            move(n-1, A, C, B);//每次都是输出A->C,所以要看到A->B,就需要将B和C交换

            if(n==Hanoi.n)
                System.out.println("前面完成(n-1)层:从A移动到B");
            System.out.println(A + " -> " + C);
            sum++;
            if(n==Hanoi.n)
                System.out.println("完成第(n)层:从A移动到C");

            move(n-1, B, A, C);//每次都是输出A->C,所以要看到B->C,就需要将A和B交换
            if(n==Hanoi.n)
                System.out.println("前面完成(n-1)层:从B移动到C");
        }
    }

}

执行结果:

3层汉诺塔的移动顺序:

A -> C

A -> B

C -> B

前面完成(n-1)层:从A移动到B

A -> C

完成第(n)层:从A移动到C

B -> A

B -> C

A -> C

前面完成(n-1)层:从B移动到C

汉诺塔移动次数:7

先完成(n-1)层:从A移动到B,

再完成第(n)层:从A移动到C,

最后完成(n-1)层:从B移动到C。

通过数学推导汉诺塔移动次数

递归算法可以通过递归式的方式去推导证明,现在通过递归式推导汉诺塔移动次数。

假定n是盘子的数量,T(n)是移动n个圆盘的移动次数。

当n=1时,T(1)=1

当n=2时,T(2)=2T(1)+1

当n=3时,T(3)=2T(2)+1

汉诺塔递归式

由递归式求n阶汉诺塔移动次数:

由递归式可知:

image

又因当n=1时,T(1)=1,得:

解得n阶汉诺塔移动次数为: 次。

汉诺塔与二进制

公式

这就像是n位二进制的和,最终得到n位二进制的最大值(全1)

所以有,n阶汉诺塔移动次数等于n位二进制得最大值,如:4阶汉诺塔移动次数为

每个盘子的移动次数,观察下图:

image

如图可知,每个盘子移动总次数刚好相反,

所以,n阶汉诺塔的第i个盘子总的移动次数为:

3阶汉诺塔图解与二进制关系

image

汉诺塔与满二叉树

递归算法会有相对应的递归树,而汉诺塔的递归树刚好是满二叉树,即所有分支结点都有两个叶子结点。

调整汉诺塔对算法代码的输出信息后:

public class Hanoi {
    // 阶数
    private static int n = 3;
    public static void main(String[] args) {
        System.out.println(String.format("%s层汉诺塔的移动顺序:", n));
        int sum = moveTree(n, 'A','B','C');
        System.out.println("汉诺塔移动次数:"+sum);
    }

    /**
     * 汉诺塔与满二叉树
     * (n-1) A -> B
     *   n   A -> C
     * (n-1) B -> C
     * 
     * 结束条件为:当n=1 时, A -> C
     */
    public static int moveTree(int n,char A, char B, char C) {
        if(n==1)
            System.out.println(String.format("第 %s 层(叶子节点):%s -> %s",n, A, C));
        else {
            moveTree(n-1, A, C, B);//每次都是输出A->C,所以要看到A->B,就需要将B和C交换

            if(n==Hanoi.n)
                System.out.println(String.format("第 %s 层(根节点):%s -> %s", n, A, C));
            else
                System.out.println(String.format("第 %s 层(分支结点):%s -> %s", n, A, C));

            moveTree(n-1, B, A, C);//每次都是输出A->C,所以要看到B->C,就需要将A和B交换
        }
        //汉诺塔的移动次数为: 2^n-1
        return (int) Math.pow(2, n)-1;
    }

}

3层汉诺塔的移动顺序:

第 1 层(叶子节点):A -> C

第 2 层(分支结点):A -> B

第 1 层(叶子节点):C -> B

第 3 层(根节点):A -> C

第 1 层(叶子节点):B -> A

第 2 层(分支结点):B -> C

第 1 层(叶子节点):A -> C

汉诺塔移动次数:7

3阶汉诺塔对应的满二叉树:

image

3阶汉诺塔的移动步骤为满二叉树的中序遍历:AC、AB、CB、AC、BA、BC、AC

从输出结果可以看到,汉诺塔盘子编号对应满二叉树自底向上计算的层号,如:1号盘子的移动对应是叶子节点,最底层盘子对应根节点。

为了更好理解,可以写成这样:

    public static int moveTree(int n,char A, char B, char C) {
        if(n==1)
            System.out.println(String.format("第 %s 层(叶子节点):%s -> %s",Hanoi.n-n+1, A, C));
        else {
            moveTree(n-1, A, C, B);//每次都是输出A->C,所以要看到A->B,就需要将B和C交换

            if(n==Hanoi.n)
                System.out.println(String.format("第 %s 层(根节点):%s -> %s", Hanoi.n-n+1, A, C));
            else
                System.out.println(String.format("第 %s 层(根节点):%s -> %s", Hanoi.n-n+1, A, C));

            moveTree(n-1, B, A, C);//每次都是输出A->C,所以要看到B->C,就需要将A和B交换
        }
        //汉诺塔的移动次数为: 2^n-1
        return (int) Math.pow(2, n)-1;
    }

汉诺塔递归实现与二叉树中序遍历的递归实现,在代码实现上很类似

public static void inorder(TreeNode root) {
    if (root == null)
        return;
    inorder(root.left);
    System.out.print(root.val);
    inorder(root.right);
}

汉诺塔的移动步骤可以用满二叉树的中序遍历来表示,反过来,我们可以通过满二叉树的特性推导出汉诺塔的一些特性:

  • 满二叉树总的结点数为,所以汉诺塔移动次数为;

  • 满二叉树第n层的节点数为,所以n阶汉诺塔第i个盘子被移动的次数为;

  • 满二叉树叶子节点数为,所以汉诺塔第一个盘子被移动的次数为;

  • 满二叉树是二进制的一种表现形式,所以汉诺塔也是二进制的一种表现形式,其中汉诺塔的移动过程就是二进制的累加过程。

最后附上三者的关系图

image

总结

如果这些结论都是自己推导发现的话,你会发现充满惊喜。其推导过程非常有意思,好像冥冥之中万物都和二进制相关。文章想表达的不仅仅是得出汉诺塔有哪些特性,更重要的是希望能在学习中,发现学习本身的乐趣,从而滋养内在的好奇心、探索精神,不断地自我推进,让学习越来越有趣越有动力。 

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

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

相关文章

PostgreSQL PG16 逻辑复制在STANDBY 上工作 (译)

开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共…

【Ubuntu18.04免密码登录SSH】

Ubuntu18.04免密码登录SSH 1 查看Ubuntu18.04系统中是否存在SSH服务2 配置SSH2.1 先删除一下ssh的目录,重新配置2.2 生存公钥和私钥2.3 将公钥上传到需要登录的服务器2.4 测试登录 1 查看Ubuntu18.04系统中是否存在SSH服务 sudo ps -e |grep ssh没有的话&#xff0…

【Vue3+Ts+Vite】配置滚动条样式

一、先看效果 二、直接上代码 <template><div class"main-container"><h1 v-for"index in 50" :key"index">这是home页面</h1></div> </template> <style lang"scss" scoped> .main-conta…

Stephen Wolfram:类似人类任务的模型

Models for Human-Like Tasks 类似人类任务的模型 The example we gave above involves making a model for numerical data that essentially comes from simple physics—where we’ve known for several centuries that “simple mathematics applies”. But for ChatGPT w…

SSTI学习的笔记(橙子)

放心&#xff0c;我会一直陪着你 一.在终端的一些指令1.虚拟环境2.docker容器 二.SSTI相关知识介绍1.魔术方法2.python如何执行cmd命令3.SSTI常用注入模块(1)文件读取(2)内建函数eval执行命令(3)os模块执行命令(4)importlib类执行命令(5)linecache函数执行命令(6)subprocess.Po…

暖手宝UL认证 亚马逊UL测试报告 UL499测试项目

UL499测试内容&#xff1a;1、 漏电流测试 2、 输入测试 3、 潮态下漏电流测试4、正常温升测试 5、 耐高压测试 6、 稳定性测试7、异常测试&#xff08;DRY&#xff09;8、 异常测试  9、 静压及强度测试10、 烧熔断器测试、 电源线拉力测试11、 电源线推力测试12、 塑件变…

二、前端高德地图、渲染标记(Marker)引入自定义icon,手动设置zoom

要实现这个效果&#xff0c;我们先看一下目前的页面展示&#xff1a; 左边有一个图例&#xff0c;我们可以方法缩小地图&#xff0c;右边是动态的marker标记&#xff0c;到时候肯定时候是后端将对应的颜色标识、文字展示、坐标点给咱们返回、我们肯定可以拿到一个list&#xf…

怎么清空电脑回收站?教您这三招(附上清空的回收站数据怎么恢复教程)

电脑回收站是一个临时存放被删除文件的地方&#xff0c;如果不清空回收站&#xff0c;这些文件仍然占用磁盘空间。清空回收站可以释放这些空间&#xff0c;提高磁盘的可用空间。那么&#xff0c;怎么清空电脑回收站的东西呢&#xff1f;清空的回收站数据怎么恢复呢&#xff1f;…

ICMP隐蔽隧道攻击分析与检测

• ICMP隧道攻击工具特征分析 一、原理 由于ICMP报文自身可以携带数据&#xff0c;而且ICMP报文是由系统内核处理的&#xff0c;不占用任何端口&#xff0c;因此具有很高的隐蔽性。 通过改变操作系统默认填充的Data&#xff0c;替换成自己构造的数据&#xff0c;这就是ICMP隐…

测试开发第一章、软件测试介绍

一、什么是软件测试 最常见的理解是:软件测试就是找BUG,发现缺陷。 软件测试就是验证软件产品特性是否满足用户的需求。 从这话我们可以看出以下两点: 测试试图验证软件是“工作的”,也就是验证软件功能执行的正确性测试的活动是以测试人员“预期的结果”为依据,这里的…

centos7 部署 k8s 1m2n

1 系统环境准备 1.1 安装所需工具 yum -y install vim yum -y install wget# 设置yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo1.2 修改…

【性能优化篇】.ttf字体包过大引起的网页加载过慢 font-spider 压缩字体包 适用于任何前端项目

背景 项目使用的是阿里巴巴普惠2.0字体&#xff0c;型号分别是 35-thin 和 45-light 这两个字体包 都是 8mb 左右 本地加载的时候可能速度不会收影响&#xff0c;发到生产和测试环境下&#xff0c;速度会很慢&#xff0c;尤其是测试环境&#xff0c;字体包加载了一分钟&#…

详细分析Python中运算符“==“和“is“的区别

目录 Python中运算符"" Python中运算符"is" ""和"is"的区别 总结 Python中运算符"" 在Python中&#xff0c;双等号运算符"" 用于比较两个值是否相等。它返回一个布尔值&#xff0c;即True或False&#xff0c;…

自动完成网页局部区域截图的方法

一般网页包含标题、导航、正文、图片、广告、外链等内容&#xff0c;但只有正文内容对我们才有价值&#xff0c;或者我们只关心其中的数据分析图表。希望把网页有价值的区域截图下来&#xff0c;在手工截图时&#xff0c;可以利用截图工具选择截图区域&#xff0c;那么能自动截…

如何评估自动化测试脚本的编写时间和维护工作量?

一、如何评估自动化测试脚本的编写时间和维护工作量&#xff1f; 评估自动化测试脚本的编写时间和维护工作量是一个复杂的过程&#xff0c;需要综合考虑以下因素&#xff1a; 脚本复杂性&#xff1a;评估脚本的复杂性&#xff0c;包括测试需求的复杂程度、涉及的功能和模块的复…

简单认识Redis 数据库的高可用

文章目录 一、Redis 高可用&#xff1a;1.简介&#xff1a;2、在Redis中实现高可用的技术 二、Redis持久化&#xff1a;1.持久化的功能&#xff1a;2.Redis 提供两种方式进行持久化&#xff1a; 三、RDB 持久化&#xff1a;1.简介&#xff1a;2.触发条件&#xff1a;4.启动时加…

LeetCode 2050. 并行课程 III:DFS

【LetMeFly】2050.并行课程 III&#xff1a;DFS 力扣题目链接&#xff1a;https://leetcode.cn/problems/parallel-courses-iii/ 给你一个整数 n &#xff0c;表示有 n 节课&#xff0c;课程编号从 1 到 n 。同时给你一个二维整数数组 relations &#xff0c;其中 relations[…

jpa生成实体类,jpa根据数据库表生成实体类

jpa生成实体类&#xff0c;jpa根据数据库表生成实体类jpa根据数据库表结构生成实体idea下SpringbootJPA从数据库自动生成实体类JPA用数据库表直接生成实体类Spring boot整合jpa(一),根据表生成实体IDEA下SpringData-JPA根据数据库表生成实体类idea怎么根据数据库表自动生成JPA实…

为什么你在用 ChatGPT 的提示词 Prompt 似乎效果不如人意?

“ 在使用ChatGPT的神奇提示词Prompt时&#xff0c;或许你会发现它的效果并不总是如人所愿。让我们看看其中的原因&#xff0c;以及如何避免这类问题。” 01 — 最近继续在研究以大模型人工智能LLM为大脑的专属知识库的开发技术。偶然看到这么一个智力游戏题目&#xff0c;让大…

HummerRisk V1.3.0 发布

HummerRisk V1.3.0发布&#xff1a; 大家好&#xff0c;HummerRisk 1.3.0和大家见面了&#xff0c;在这个版本中我们继续在多云接入管理、多云检测方式、云资源态势方面提供新的能力&#xff0c;并增加了新的镜像仓库支持类型&#xff0c;并优化了云的区域选择、优化规则组内容…