算法介绍
其实算法非常简单,当盘子的个数为n时,移动的次数应等于2^n – 1(有兴趣的可以自己证明试试看)。后来一位美国学者发现一种出人意料的简单方法,只要轮流进行两步操作就可以了。首先把三根柱子按顺序排成品字型,把所有的圆盘按从大到小的顺序放在柱子A上,根据圆盘的数量确定柱子的排放顺序:若n为偶数,按顺时针方向依次摆放 A B C;
若n为奇数,按顺时针方向依次摆放 A C B。
⑴按顺时针方向把圆盘1从现在的柱子移动到下一根柱子,即当n为偶数时,若圆盘1在柱子A,则把它移动到B;若圆盘1在柱子B,则把它移动到C;若圆盘1在柱子C,则把它移动到A。
⑵接着,把另外两根柱子上可以移动的圆盘移动到新的柱子上。即把非空柱子上的圆盘移动到空柱子上,当两根柱子都非空时,移动较小的圆盘。这一步没有明确规定移动哪个圆盘,你可能以为会有多种可能性,其实不然,可实施的行动是唯一的。
⑶反复进行⑴⑵操作,最后就能按规定完成汉诺塔的移动。
所以结果非常简单,就是按照移动规则向一个方向移动金片:
如3阶汉诺塔的移动:A→C,A→B,C→B,A→C,B→A,B→C,A→C
汉诺塔问题也是程序设计中的经典递归问题,下面我们将给出递归和非递归的不同实现源代码。
首先给出递归实现的C++代码:
#include <cstdlib>
#include <iostream>
using namespace std;
void move(int n,char a,char b)
{
cout<<"ÒÆ¶¯µÚ"<<n<<"¸öÅÌ×Ó"<<"´Ó"<<a<<"µ½"<<b<<endl;
}
void hanoi(int n,char a,char b,char c)
{
if(n==1) move(n,a,c);
else
{
hanoi(n-1,a,c,b);
move(n,a,c);
hanoi(n-1,b,c,a);
}
}
int main(int argc, char *argv[])
{
int n;
cout<<"Óм¸¸öÅÌ×Ó"<<endl;
cin>>n;
cout<<endl;
hanoi(n,'A','B','C');
system("PAUSE");
return EXIT_SUCCESS;
}
下面给出非递归实现的代码:
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
//第0位置是柱子上的塔盘数目
int zhua[100]={0},zhub[100]={0},zhuc[100]={0};
char charis(char x,int n)
//左右字符出现顺序固定,且根据n值奇偶尔不同
{
switch(x)
{
case 'A':
return (n%2==0)?'C':'B';
case 'B':
return (n%2==0)?'A':'C';
case 'C':
return (n%2==0)?'B':'A';
default:
return '0';
}
}
void print(char lch,char rch)
//打印字符
{
if(lch=='A')
{
switch(rch)
{
case 'B':
zhub[0]++;
zhub[zhub[0]]=zhua[zhua[0]];
zhua[zhua[0]]=0;
zhua[0]--;
break;
case 'C':
zhuc[0]++;
zhuc[zhuc[0]]=zhua[zhua[0]];
zhua[zhua[0]]=0;
zhua[0]--;
break;
default:
break;
}
}
if(lch=='B')
{
switch(rch)
{
case 'A':
zhua[0]++;
zhua[zhua[0]]=zhub[zhub[0]];
zhub[zhub[0]]=0;
zhub[0]--;
break;
case 'C':
zhuc[0]++;
zhuc[zhuc[0]]=zhub[zhub[0]];
zhub[zhub[0]]=0;
zhub[0]--;
break;
default:
break;
}
}
if(lch=='C')
{
switch(rch)
{
case 'A':
zhua[0]++;
zhua[zhua[0]]=zhuc[zhuc[0]];
zhuc[zhuc[0]]=0;
zhuc[0]--;
break;
case 'B':
zhub[0]++;
zhub[zhub[0]]=zhuc[zhuc[0]];
zhuc[zhuc[0]]=0;
zhuc[0]--;
break;
default:
break;
}
}
printf("\t");
int i;
printf("(");
for(i=1;i<=zhua[0];i++)
printf(" %d ",zhua[i]);
printf(")");
printf("(");
for(i=1;i<=zhub[0];i++)
printf(" %d ",zhub[i]);
printf(")");
printf("(");
for(i=1;i<=zhuc[0];i++)
printf(" %d ",zhuc[i]);
printf(")");
printf("\n");
}
void Hannuo(int n)
{
//分配2^n个空间
bool *isrev;
isrev=(bool *)malloc(sizeof(bool)*(int)pow(2,n));
for(int i=1;i<pow(2,n);i++)
isrev[i]=false;
//循环计算是否逆序
for(int ci=2;ci<=n;ci++)
{
for(int zixh=(int)pow(2,ci-1);zixh<pow(2,ci);zixh+=4)
//初始值重复一次,自增值可加4,减少循环次数。
isrev[zixh]=isrev[(int)pow(2,ci)-zixh];
//该位置为中间位置,且奇次幂逆序,偶数幂不逆序
isrev[(int)pow(2,ci)]=((ci-1)%2==0)?false:true;
}
char lch='A',rch;
rch=(n%2==0?'B':'C');
printf("%d\t",1);
printf("%c->%c",lch,rch);
print(lch,rch);
for(int k=2;k<pow(2,n);k++)
{
if(k%2==0)
rch=charis(lch,n);
else
lch=charis(rch,n);
printf("%d\t",k);
if(isrev[k])
{
printf("%c->%c",rch,lch);
print(rch,lch);
}
else
{
printf("%c->%c",lch,rch);
print(lch,rch);
}
}
}
int main()
{
int n;
printf("Input the num:");
scanf("%d",&n);
zhua[0]=n;
汉诺塔
for(int i=1;i<=n;i++)
zhua[i]=n-i+1;
Hannuo(n);
return 0;
}实现的截图如下:

本文详细介绍了汉诺塔问题的算法原理,并提供了递归和非递归两种实现方式的源代码。同时,文章还通过实现截图展示了算法运行效果。

被折叠的 条评论
为什么被折叠?



