汉诺塔的非递归算法

news/2024/11/6 17:22:24/

对于汉诺塔问题,我们都普遍认为这个是一个典型的递归问题,然而递归需要使用到系统对应的栈,开销比较大,因此我在想使用非递归算法来解决它,然而网上绝大部分的教程都是自己模拟了一个栈,因此我在考虑写一篇blog记录一下。

问题描述

将一个柱子中的所有圆盘移动到另一个柱子,移动过程需遵守以下规则:每次只能移动一个圆盘,而且只能移动某个柱子上最顶部的圆盘;移动过程中,必须保证每个柱子上的大圆盘都位于小圆盘的下面。
在这里插入图片描述

递归算法

这个算法还算比较好理解,我们计A,B,C盘,那么假设我们要把n圆盘,从A移到C,可以拆分为小问题,我们把上面n-1个的圆盘看成整体,让它先到B,那么最下面的一个移动到C,再把n-1个从B移动到C。
这样就把复杂问题,拆分成更简单的问题,那么退出条件就是只剩下一个圆盘,即为,A->C

#include<iostream>
using namespace std;
//算法设计实验1,汉诺塔问题
//采用递归方法解决
//n个棋盘  A B C为基座   最终达成目标A全部移到C
//性能 T(n)=2T(n-1)+1  1忽略 那么时间复杂度为O(2^n),相当的大,因此对于n较大的时候,运行时间较长
void Hanoi1(int n,char A,char B,char C)
{if (n == 1){cout  << A << " -> "<< C << endl;return;}Hanoi1(n - 1, A, C, B);//相当于先把上面n-1盘子,移到中间cout<< A << " -> " << C << endl;Hanoi1(n - 1, B, A, C);//相当于先把上面n-1盘子,移到中间
}
int main()
{int n;cout << "请选择汉诺塔层数:";cin >> n;//递归算法Hanoi1(n, 'A', 'B', 'C');return 0;
}

非递归运行结果

非递归算法

当然我参考的时候发现,很多人模拟了栈作为非递归算法,这种当然可以,但是其核心思想还是用栈的方法,实现递归,本质上和系统的栈思想一致。因此我在考虑用真正非递归的思想来解决这个问题。
由于问题较为复杂,因此咱们先举出比较简单的例子。
假设只有2个盘子
在这里插入图片描述

步骤1234567
移动的盘子0102010
步骤0->20->12->10->21->01->20->2

我们可以发现其中是有规律的:

  1. 如果把三个柱子围成一个环,盘子总数为N,
  2. 如果N为偶数:奇数号盘每次2步;偶数号盘每次1步;
  3. 如果N为奇数:奇数号盘每次1步;偶数号盘每次2步;
  4. 当三个柱子都不为空则优先移动最小的盘子。
#define N 150
#define X dish[i].dnum
#define Y dish[i].lnum
#define SP dish[i].peg
//向右走一步
struct dish
{int dnum;	//自身编号int lnum;	//下面的盘子int peg;	//所在的柱子
}dish[N];
//初始化每个柱的状态为空
int s[3] = {};
//向右走一步
int mov1(int sp, int x)
{if (s[(sp + 1) % 3] > x)//要移动的目标是可以承载当前盘子的{//printf("\nmove1\n");printf("%d %d --> %d\n", x, sp, (sp + 1) % 3);s[dish[x].peg] = dish[x].lnum; //x盘所在柱子减去当前盘,顶盘变为x盘的下一个dish[x].lnum = s[(dish[x].peg + 1) % 3]; //x盘的落点即x转移后的下面的盘s[(dish[x].peg + 1) % 3] = dish[x].dnum; //x盘转移到的柱子顶盘值变为x盘值dish[x].peg = (dish[x].peg + 1) % 3; //x盘所在柱子变为落点柱子return 1; //转移成功}else return 0; //转移失败
}
//向右走两步
int mov2(int sp, int x)
{if (s[(sp + 2) % 3] > x)//要移动的目标是可以承载当前盘子的{//printf("\nmove2\n");printf("%d %d --> %d\n", x, sp, (sp + 2) % 3);s[dish[x].peg] = dish[x].lnum; //x盘所在柱子减去当前盘,顶盘变为x盘的下一个dish[x].lnum = s[(dish[x].peg + 2) % 3]; //x盘的落点即x转移后的下面的盘s[(dish[x].peg + 2) % 3] = dish[x].dnum; //x盘转移到的柱子顶盘值变为x盘值dish[x].peg = (dish[x].peg + 2) % 3; //x盘所在柱子变为落点柱子return 1; //转移成功}else return 0; //转移失败
}
int main()
{int n;cout << "请选择汉诺塔层数:";cin >> n;int i, change = 1, j;for (i = 0; i < n; i++){dish[i].dnum = i; //作为盘子的唯一标识,顶层为0,最高级的最大盘子为N-1dish[i].lnum = i + 1; //底层盘子的下一层为N,即空柱dish[i].peg = 0; //初始化,所有盘子都在A柱	}s[0] = 0; //当前0柱上最顶端的盘子值s[1] = n;s[2] = n;//空柱,屏蔽大于n的盘子while (s[0] < n || s[1] < n){j = 0;for (i = 0; s[j] <= n && i < n; i++) //不为空柱则向下找盘子{if (s[0] >= n && s[1] >= n)return;if (s[dish[i].peg] != dish[i].dnum) //当上面有盘子时不能移动continue;if ((n % 2 == 0 && X % 2 == 0) || (n % 2 == 1 && X % 2 == 1))change = mov1(SP, X);else change = mov2(SP, X);if (!change){if (s[0] < n && s[1] < n && s[2] < n){for (int k = 0; s[k] != 0; k++)j = k;}else j = (j + 1) % 3; //该柱上没有盘子可以移动则更换柱子}}}return 0;
}

因此可以得到非递归版本的,汉诺塔答案!


http://www.ppmy.cn/news/50172.html

相关文章

这些美国名校的AI人工智能大牛,你知道几个?

CS专业被US News评为就业TOP 100职业第一名、STEM职业第一名、技术类职业第一名。 AI人工智能&#xff0c;随着GPT的横空出世已成为最热门的CS专业。“深度学习”和“神经网络”等是新一代人工智能的重要代表&#xff0c;如今在面部识别、语音输入、基因医疗等重要领域被广泛应…

Linux嵌入式uboot使用tftp网络启动加载zImage、设备树

文章目录 一、前言二、Linux U-boot 相关命令&#xff08;1&#xff09;help 命令&#xff08;2&#xff09;printenv 命令&#xff08;3&#xff09;setenv 函数&#xff08;4&#xff09;saveenv 函数 三、tftp启动linux内核步骤&#xff08;1&#xff09;进入u-boot模式&…

Three.js教程:透视投影相机

推荐&#xff1a;将NSDT场景编辑器加入你的3D工具链。 其他系列工具&#xff1a;NSDT简石数字孪生 Threejs如果想把三维场景Scene渲染到web网页上&#xff0c;还需要定义一个虚拟相机Camera&#xff0c;就像你生活中想获得一张照片&#xff0c;需要一台用来拍照的相机。 透视…

5、Hash对称加密

一、Hash Hash&#xff0c;一般翻译做“散列”&#xff0c;也有直接音译为“哈希”的&#xff0c;就是把任意长度的输入通过散列算法变换成固定长度的输出&#xff0c;该输出就是散列值。这种转换是一种压缩映射&#xff0c;也就是&#xff0c;散列值的空间通常远小于输入的空…

RocketMQ-02

1. RocketMQ集群搭建 1.2 集群搭建方式 1.2.1 集群特点 NameServer是一个几乎无状态节点&#xff0c;可集群部署&#xff0c;节点之间无任何信息同步。 Broker部署相对复杂&#xff0c;Broker分为Master与Slave&#xff0c;一个Master可以对应多个Slave&#xff0c;但是一个…

华为OD机试真题(Java),喊7的次数重排(100%通过+复盘思路)

一、题目描述 喊7是一个传统的聚会游戏&#xff0c;N个人围成一圈&#xff0c;按顺时针从1到N编号。 编号为1的人从1开始喊数&#xff0c;下一个人喊的数字为上一个人的数字加1&#xff0c;但是当将要喊出来的数字是7的倍数或者数字本身含有7的话&#xff0c;不能把这个数字直…

Rabbit与springboot整合-1

目录 1、整体结构 2、pom引入 3、配置文件 4、代码 公共类 controller类 JSON转换类 监听-接收发送消息类 1、整体结构 2、pom引入 <!--rabbitmq--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-st…

【Linux命令行与Shell脚本编程】第七章 Linux文件权限

Linux命令行与Shell脚本编程 第七章 Linux文件权限 文章目录 Linux命令行与Shell脚本编程七,Linux文件权限7.1,Linux的安全性7.1.1,用户信息 /etc/passwd文件7.1.2,用户密码 /etc/shadow文件7.1.3,useradd 添加新用户7.1.4,userdel 删除用户7.1.5, 修改用户1,usermod2,passwd…