Day 27 —— Hanoi
1. Background
今天是学习java的第27天,学习的内容是hanoi。
由于最近毕业设计业务繁忙,所以不得已中断了一些天的代码学习。现在补上,我的计划是在群里的群友复试之前学到第50天,也就是把数据结构的内容学完,然后专心去复习初试。
2. Description
2.1 Hanoi
汉诺塔作为我们的老朋友了,每一次学习新的计算机语言,每次讲递归老师都会讲一遍这个。
我对它的理解是有三根签签,第一根签签上串很多盘盘,大的在下,小的在上。我们的最终目标是把第一根签签上的盘盘全部移到最后一根上面去,移动过程中也要遵循大的在上小的在下规则。
2.2 操作
这个我讲一下我的思路吧,之前学C语言的时候被这个虐了好几天,把当时老师讲的每一个字都记得清清楚楚。
由于闵老师这里只用了三个盘盘,可能不具有代表性。我现在要讲思路嘛,直接极端一点,拿64个盘盘来举例(当年给我讲C语言的那个老师就是拿的64个盘)。
首先对于汉诺塔的玩法可以简单分为三个步骤:
- 将第一根签签上前63个盘盘从X移动到Y上,确保大盘在小盘下。(为了叙述方便直接把第一个盘盘命名为X,第二个命名为Y,第三个为Z)
- 将X上面剩下的那个第64个盘子移到Z上。
- 将Y上的63个盘盘移到Z上。
好的,我们现在就把64个盘的问题转为了63个盘的问题,所以我们把这个思路聚集为下面的两个问题:
- 如何将X上的63个盘盘移到Y上。(通过Z)
- 如何将Y上的63个盘移到Z上。(通过X)
然后我们来讨论第一个问题:
- 将X上前62个盘盘从X移动到Z上,确保大盘在小盘下。
- 将X上面剩下的那个第63个盘子移到Y上。
- 将Z上的62个盘盘移到Z上。
第二个问题同上:
- 将Y上前62个盘盘从Y移动到X上,确保大盘在小盘下。
- 将Y上面剩下的那个第63个盘子移到Z上。
- 将X上的62个盘盘移到Z上。
这么分析下来递归的味道就出来了。下面直接进入代码。
3. Code
package datastructure;
/**
* Hanoi tower.
*
* @author Leo liu.
*/
public class Hanoi {
/**
***********
* 汉诺塔主体代码
*
* @param paraFirst 第一根签签。
* @param paraMiddle 第二根签签。
* @param paraLast 第三根签签。
* @param paraNum 盘子的数量。
* **********
*/
public static void hanoi(char paraFirst, char paraMiddle, char paraLast, int paraNum) {
if (paraNum == 1) {
System.out.println(paraFirst + "->" + paraLast + " ");
return;
}
hanoi(paraFirst, paraLast, paraMiddle, paraNum - 1);
System.out.println(paraFirst + "->" + paraLast + " ");
hanoi(paraMiddle, paraFirst, paraLast, paraNum - 1);
} // Of hanoi
public static void main(String[] args) {
hanoi('a', 'b', 'c', 3);
} // Of main
} // Of Hanoi
运行结果:
4. Summarize
可能是之前学习汉诺塔的时候递归的思想深入我心了吧,二叉树反而没有什么感觉了。下去还得继续琢磨。
对于这个我特地把我两年前学习C语言的代码翻了出来,可惜的是电脑重装系统之后已经没有C语言编译环境了,所以没有运行查看结果。
#include <stdio.h>
#define MAX_NUM 64
int schedule[MAX_NUM+1][MAX_NUM+1];
int arrange(int begin, int num);
int arrange(int begin, int num)
{
int i, j;
if (num == 2)
{
schedule[begin][1] = begin;
schedule[begin][2] = begin + 1;
schedule[begin+1][1] = begin + 1;
schedule[begin+1][2] = begin;
return 0;
}
arrange(begin, num/2);
arrange(begin + num/2, num/2);
for (i = begin + num/2; i < begin + num; i++)
{
for (j = num/2 + 1; j <= num; j++)
{
schedule[i][j] = schedule[i-num/2][j-num/2];
}
}
for (i = begin; i < begin + num/2; i++)
{
for (j = num/2 + 1; j <= num; j++)
{
schedule[i][j] = schedule[i+num/2][j-num/2];
}
}
}
int main(void)
{
int num, i, j,flag = 1;
char name[][128] = {0};
printf("请输入参赛的队伍数量:");
scanf("%d", &num);
for(int i = 0; i < num; i++)
{
printf("请输入第%d个队名", i + 1);
scanf("%s", &name[i]);
getchar();
}
// 检查num是否2的N次方
// 注意,这里是&,不是&&
// &是按位与操作,1&1==1,0&1==0,0&0 == 0
if (num & num - 1)
{
printf("参数队伍的数量必须是2的N次方!\n");
return -1;
}
arrange(1, num);
printf("编 号");
for (i = 1; i < num; i++)
{
printf("\t第%d天", i);
}
putchar('\n');
for (i = 1; i <= num; i++)
{
for (j = 1; j <= num; j++)
{
printf("%s\t", *(name + schedule[i][j] - 1));
}
putchar('\n');
}
return 0;
}
对了,这里说一下。代码中的提示词和注释最好还是用英文,用中文的很有可能会出现乱码。下图是我用VScode打开的画面,我猜想是我之前学习C语言的编译器的编码和VScode的不同,然后把文件用记事本打开,由于记事本有自动选取编码的功能,所以成功的看到了上面没有乱码的代码。
记事本上的编码显示的是ANSi,然后VScode默认是UTF-8的,所以出现了乱码。