java 汉诺塔Hanoi递归、非递归(仿系统递归)和非递归规律 实现代码
发布时间:2023-09-13 11:29:31
标签:汉诺塔,Hanoi
程序如下:
View Code
/*
* Hanoi塔游戏 问题描述:
* 汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。
* 大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照
* 大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小
* 顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在
* 三根柱子之间一次只能移动一个圆盘。
*
* fuction:实现 hanoi塔
* 1.递归实现
* 2.非递归实现
* author:iGeneral
* date:2013.04.26
*
* expe:
* 1.注意:塔的状态:当status=1时,表示可以直接将该Disk移动到目标塔
* 而不是用Disk的id来判断输出
* 2.System.out.println();
System.out.println((int)3.3%3);
没有(int)时,输出:0.299999
加上(int)后,输出:0
*/
package part03.chapter10;
import java.util.Scanner;
public class _2exercise {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入Hanoi碟子的数量:");
int diskNum = scanner.nextInt();
Hanoi hanoi = new Hanoi();
System.out.println("递归实现:");
hanoi.play_recursive(diskNum, 'A', 'B', 'C');
System.out.println("非递归实现(模仿递归思想):");
hanoi.play_non_recursive(diskNum);
System.out.println("非递归实现(根据Hanoi规律):");
hanoi.play_regular(diskNum);
}
}
class Hanoi {
// 递归实现
public void play_recursive(int num, char A, char B, char C) {
if (num == 1) {
System.out.println(A + " -> " + C);
return;
} else {
play_recursive(num - 1, A, C, B);
System.out.println(A + " -> " + C);
play_recursive(num - 1, B, A, C);
}
}
// 非递归实现:模仿递归思想
public void play_non_recursive(int diskNum) {
Stack stack = new Stack();
stack.push(new Disk(diskNum, 'A', 'B', 'C'));
Disk popDisk = null;
while ((popDisk = stack.pop()) != null) {
if (popDisk.status == 1) {
System.out.println(popDisk.A + " -> " + popDisk.C);
} else {
// 反顺序添加
// 将执行移动 popDisk 的下一步的Disk添加到Stack
stack.push(new Disk(popDisk.status - 1, popDisk.B, popDisk.A,
popDisk.C));
// 将一个status为 "1" 且移动顺序与 popDisk 相同的Disk 添加到Stack中
stack.push(new Disk(1, popDisk.A, popDisk.B, popDisk.C));
// 将执行移动 popDisk 的前一步的Disk添加到Stack中
stack.push(new Disk(popDisk.status - 1, popDisk.A, popDisk.C,
popDisk.B));
}
}
}
// 非递归实现:根据Hanoi规律
public void play_regular(int diskNum) {
// 根据规律,需要根据 Disk 的个数,多塔的位置进行调整
// 塔的个数为偶数时,将三个塔按“A->B->C”的顺序排列成三角形
// 塔的个数为奇数时,将三个塔按"A->C->B"的顺序排列成三角形
// 将diskNum个Disk按”上小下大“的顺序放在A塔中(堆栈实现),同时将B塔和C塔置空
Stack_play_regular A = new Stack_play_regular('A');
Stack_play_regular B = new Stack_play_regular('B');
Stack_play_regular C = new Stack_play_regular('C');
for (int i = diskNum; i > 0; i--) {
A.push(i);
}
// 将三个塔模拟成三角形形状排列
Stack_play_regular[] towers = new Stack_play_regular[3];
towers[0] = A;
if (diskNum % 2 == 0) {
towers[1] = B;
towers[2] = C;
} else {
towers[1] = C;
towers[2] = B;
}
// 最小Dish所在的塔,通过该塔在towers中的
int towerOfMinimunDisk = 0;
// 根据证明:n个Disk移动完成至少需要2^n-1次
// 不断交替进行以下两步
// 将最小的Disk按以上塔的顺序下移到下一个塔
// 对除了最小Disk所在的塔的另外两个塔进行操作,可能出现两种情况
// 情况一:一个塔中没有Disk,此时将存在Disk的塔最上面的Disk移动到没Disk的塔上
// 情况二:两个塔都有Disk,此时对他们最上面的塔进行比较,将较小的Disk移动到较大的Disk上
// 不会存在两个塔都没有Disk的情况,除非移动已经完成或未开始或只有一个盘子时的移动
int ii = 0;
for (int i = 0; i < (Math.pow(2, diskNum) - 1);) {// --------------注意在此处不进行i++
// 取出三个塔,使代码更清晰
Stack_play_regular tower = towers[towerOfMinimunDisk];
Stack_play_regular tower_1 = towers[(int) ((towerOfMinimunDisk + 1) % 3)];
Stack_play_regular tower_2 = towers[(int) ((towerOfMinimunDisk + 2) % 3)];
// 移动最小的盘子
System.out.println(tower.name + " -> " + tower_1.name);
tower_1.push(tower.pop());
i++;// --------------注意在此处进行i++
towerOfMinimunDisk = (int) ((towerOfMinimunDisk + 1) % 3);
// ------------注意此时对三个tower进行重新赋值
tower = towers[towerOfMinimunDisk];
tower_1 = towers[(int) ((towerOfMinimunDisk + 1) % 3)];
tower_2 = towers[(int) ((towerOfMinimunDisk + 2) % 3)];
// 对另外两个塔进行处理
if ((tower_2.getTop() != -1 && (tower_1.showTopDisk() > tower_2
.showTopDisk()))
// --------------注意要再加上 tower_2.getTop() != -1
// 进行判断,否则可能数组访问越界
|| (tower_1.getTop() == -1 && tower_2.getTop() != -1)) {
System.out.println(tower_2.name + " -> " + tower_1.name);
tower_1.push(tower_2.pop());
i++;// --------------注意在此处进行i++
} else if (((tower_1.getTop() != -1 && tower_1.showTopDisk() < tower_2
.showTopDisk()))
// --------------注意要再加上 tower_1.getTop() != -1
// 进行判断,否则可能数组访问越界
|| (tower_1.getTop() != -1 && tower_2.getTop() == -1)) {
System.out.println(tower_1.name + " -> " + tower_2.name);
tower_2.push(tower_1.pop());
i++;// --------------注意在此处进行i++
}
ii = i;
}
System.out.println(ii);
}
}
// 存放信息的结构体
class Disk {
// 从A塔通过B塔移动到C塔
char A;
char B;
char C;
// 塔的状态:当status=1时,表示可以直接将该Disk移动到目标塔
int status;
// 重写构造函数
public Disk(int status, char A, char B, char C) {
this.status = status;
this.A = A;
this.B = B;
this.C = C;
}
}
// 存放Disk的栈
class Stack {
// 用来存储盘子的数组
Disk[] disks = new Disk[10000];
// 塔顶
private int top = 0;
// 查看栈顶
public Disk stackTop() {
return disks[top];
}
// 出栈
public Disk pop() {
if (top != 0) {
top--;
return disks[top + 1];
} else {
return null;
}
}
// 入栈
public void push(Disk disk) {
top++;
disks[top] = disk;
}
}
// 为 play_regular(int diskNum) 创建的 Stack 类
// 以 diskId 来表示 Disk 对象
class Stack_play_regular {
// 塔名
char name;
// 塔顶
private int top = -1;
public int getTop() {
return top;
}
// 通过数组实现Stack,最多64个Disk
int[] stack = new int[64];
// 重写构造函数,初始化塔的名字name
public Stack_play_regular(char name) {
this.name = name;
}
// 查看栈顶
public int showTopDisk() {
if (top == -1) {
return -1;
}
return stack[top];
}
// 入栈
public void push(int diskId) {
stack[++top] = diskId;
}
// 出栈
public int pop() {
return stack[top--];
}
}


猜你喜欢
- SSL是为网络通信提供安全以及保证数据完整性的的一种安全协议,SSL在网络传输层对网络连接进行加密。例:cas 的单点登陆就用到了SSL一、
- 背景在我们实际生产容器化部署过程中,往往会遇到 Docker 镜像很大,部署发布很慢的情况影响 docker 镜像大小的因素,主要有以下三个
- 本文实例为大家分享了c语言实现可自定义的游戏地图的具体代码,供大家参考,具体内容如下博主相信每个人都有想做游戏的冲动,那么本文将给出一个用c
- 一 前言这篇文章是很基础的一文,没多大深度,对于开发人员必然是熟练于心。本篇文章的主题是为什么java要设置类成员访问级别?其原因也很简单,
- 如下所示:import java.util.Scanner;public class Main{public static void mai
- 在C#绘制中国象棋棋盘是C#程序设计中GDI+的一个重要组成部分。这也是非常考验编程技巧的操作。在绘制之前首先要对棋盘有一个完整的认识。下面
- 最近研究了一下如何在Android上实现CoverFlow效果的控件,其实早在2010年,就有Neil Davies开发并开源出了这个控件,
- 以下图中TV VOD两个按键为例,文章中所涉及到的文件只写文件名,因每个方案的路径各不相同,请自行全局搜索文件。 1.获取按键的扫
- JOL简介JOL的全称是Java Object Layout。是一个用来分析JVM中Object布局的小工具。包括Object在内存中的占用
- 写作原因:跨进程通信的实现和理解是Android进阶中重要的一环。下面博主分享IPC一些相关知识、操作及自己在学习IPC过程中的一些理解。这
- 说明:此头像类似微信群组头像,整个头像由组内前N位人员的头像组合而成,可用网络或本地图片进行组合,最终显示为一个头像整体,看效果图:一、自定
- 在 C 语言中,如果发生错误,上级函数要进行出错处理,层层上传,容易造成过多的出错处理代码,并且传递的效率比较低下。C++ 中的异常C++
- 曾经有一个朋友问过我一个问题, 一张512*512 150KB PNG格式图片和一张512*512 100KB 压缩比是8的JP
- 前言初步接触了Socket,现使其与Unity相结合,做成一个简单的客户端之间可以互相发送消息的一个Test。下面话不多说了,来一起看看详细
- 这篇文章将向大家展示Java编程利用socket多线程访问服务器文件代码示例,如果您想先了解Java多线程socket编程的基础知识,可以看
- 前言最近使用QT中的QTextEdit控件,作为实时数据显示的UI,在一次写入超过多少k的时候循环写入则会卡顿,网上也没有什么好的解决方案,
- 前言前一段时间做了一个项目,需要解决中文、繁体、英文的国际化问题,所以本文将详细介绍springboot页面国际化配置的过程方法如下1.引入
- 译者注:个人觉得用定时任务来跑垃圾回收不是很好的例子,从译者接触到的项目来看,比较常见的是用定时任务来进行非实时计算,清除临时数据、文件等。
- Java作为一面向对象的语言,具备面向对象的三大特征——继承,多态,封装。继承顾名思义,继任,承接,传承的意思。面向对象的语言有一个好处,就
- 1.问题:昨天把项目打包放到国产中间件东方通(外部容器,功能类似Tomcat)上时,发现某些请求下载文件的接口不能正确返回文件,而是返回一个