程序源码(前半部分注释请忽略)
2022/04/22/Project1/Project1/project.c · 将车/c_practice - Gitee.com
之前写过一篇关于汉诺塔问题的博客,但是那个博客只是输出了字母解,而且我现在回头看,不是完全明白自己写的是什么意思
干脆详细写一篇输出汉诺塔图形解的程序
所谓图形解,以2层汉诺塔举例如图
如上,我们程序的思路是
1,首先建立三个数组用来表示图形中的柱子和盘子
数字代表盘子大小号码,| 代表柱子
2,确定汉诺塔的层数
3,然后初始化数组
并打印初始化数组
4,利用递归拆解汉诺塔问题
接下来是各种函数的写法(执行递归的函数在最后面)
一,初始化函数 chu_shi_hua()
看了这个函数,草履虫都直呼简单,值得注意的是我们将一个字符存入整形数组中,存储的是该
字符的ASCII码值,在之后的打印函数中我们打印这个字符应该使用%c与打印数字的%d分开
我这里设置的是最大为10层塔,当然也可以更改,大于50算半天或者栈溢出
二,打印数组的函数 print()
因为我们需要的图形分为字符符号和数字符号,所以打印这样的数组需要在循环中进行多次判断
以确定需要打印的是字符还是数字
同时我们在打印函数的最后再加一个循环,用来实现每次输入1,则汉诺塔解一步
具体思路是,要求玩家输入一个数字,如果为1则跳出循环,结束打印函数,继续后面的程序
否则,就一直执行打印函数
三,移动函数 move() ********非常重要仅次于递归
思路是,在求汉诺塔的递归函数中,每当确定了需要移动的盘子在那个柱子,和移动向那个柱子后,将其传递给移动函数
移动函数,找到该取盘子的柱子上可以取的盘子,放到放盘子的柱子可以放的位置
即
当数组下标从0 -> 9逐渐增大
取的柱子的数组下标对应的元素为 ‘|’ 时 , 说明上一个数组下标对应的元素为取得盘子
同理,放的柱子下标为 ‘|’ 时就可以放
由于在执行递归的函数中,到底那个形参对应那个实参很难分清楚,而且规律并不统一
所以本人在尝试了一天直接在递归函数中打印数组之后终于放弃了
为什么不干脆多传一组参数,让这个多出来的一组参数,不因为递归而改变
使这些多出来的形参地址和实参地址直接对应便于打印
因此,我给执行递归的函数多传了一组参数,且保证,多层递归中,这些形参的地址始终与
实参的地址对应
然后,再把执行递归的函数中的多出来的一组参数传给移动函数
这样就可以实现,每次移动之后,直接打印实参数组了,而不需要去研究到底那个形参对应那个实
参,那样太痛苦了
最后,重点中的重点 **************
执行递归的函数
五,解汉诺塔 jie()
我们要把所有盘子移动过去先要把最大的盘子移动过去
要把最大的移动过去,先要把次最大的移动过去
问题逐渐缩小
所以使用一个递归
第一个递归会一直转递到只有一个盘子的时候
然后回溯
而我们注意到,每次最大盘子的摆放位置,一次是C一次是B
所以递归如下
// // jie(n - 1, a, c, b);
// //参数意为把上次2位置上的的值传给下次3位置,把3位置的值传给下一次2位置的值
// //位置上写什么即为接受上次什么位置的值
// //第一次递归为将最大的盘转移
// // 每次 b c 交换
// // 我们知道
// // 解汉诺塔主要分三部
// //
// // 1,把最大的盘子之前的盘子全部转移到一个柱子,并空出一个可以放最大盘子的柱子
// //
// // 2,把最大盘子转移到那个空柱子
// //
// // 3,因为之前的最大盘子已经转移
// //
// // 此时的情况是一个柱子放最大盘子,一个柱子放其他盘子,一个空柱子
// //
// // 只要我们认为已经被放了最大盘子的C柱子的为空柱子,就可以认为有两个空柱子,和一个放置其余盘子的柱子
// //
// // 则之前的问题把N个盘子A移动到C柱子,变成了把N-1个盘子B移动到C柱子
// //
// // 最后逐渐简化
// //
// // 如图,即为流程,要把N个盘子放到C柱子需要把N-1个放到b柱子
// // 要N-1....到B需将n-2...放到C
// //
//
a b cn
a ()bn-1 c
a b ()cn-2
........
a b3 c **
a b c2 **
a b1 c **
//
// /* printf("%d ----> %c\n", n, c);*/
//
// //每次都会打印最大的盘子到正确柱子的步转移骤
// //但如果程序就此结束,则程序只打印如何把最大盘子转移到C的步骤
// //
// // 即要把N号盘到C,需N-1号次最大盘移动到B(不考录其他每次只转移最大盘)
// // 要N-1到B,需n-2到C.....
// // 不考虑如何空出一个柱子
// // 只考虑最大盘子的行动
// //
// // 如图为最大盘的移动情况带()意味只有一个盘子括号内数字表示盘号,不带括号代表其余多少盘子
// //
// // a bn-1 c(n)
// // an-2 b c(n-1)
// // a bn-3 c(n-2)
// //...
// // a b3 c(4) 此时要求实现 a b c4 即除3号盘外2个到A
// //
// // a2 b c(3) 此时要求实现 a2 b c(3) 即除2号盘外1个到B
// //
// // a b1 c(2) 此时要求实现 a b c(1) 即除1号盘外0个到A
// //
可以看出其余盘子移动的次序是A,B互换
即,每次到了这一步,剩下的工作为,将其余盘所在柱子重新视为A柱子
放了最大盘的柱子视为空柱子
此时的问题相当于之前原问题变小了
所以再次调用递归
// //jie(n - 1, b, a, c);
// //a,b 交换(1位置和2位置交换) 3位置是打印位置
// //位置上写什么即为接受上次什么位置的值
// //意为把上次3位置上的的值传给下次1位置,把1位置的值传给下一次2位置的值,把2位置的值传给下一次3位置的值
// //
// //
// //第二次递归的意思是,在最大盘到位后,将其除了次最大盘的其余盘移动到空柱
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)