如果你不想看过程,想直接使用代码可直接移步末尾处:代码,调用示例及结果
问题简述:
据说古代有一个梵塔,塔内有三个底座A、B、C,A 座上有64 个盘子,盘子大小不等,大的在下,小的在上。有一个和尚想把这64 个盘子从A 座移到C 座,但每次只能允许移动一个盘子。在移动盘子的过程中可以利用B 座,但任何时刻3 个座上的盘子都必须始终保持大盘在下、小盘在上的顺序。如果只有一个盘子,则不需要利用B 座,直接将盘子从A 移动到C 即可。
问题分析:
假设共有Num个盘子,不难发现,必须要将上层num-1个盘子都移到b(临时底座)上,才能将最后一个盘子移到c上,那么接下来只要将Num-1个盘子再从B移到c上(这一步骤则可套用前一步将a移到b的过程,此时可以发现,b为源底座,a为临时底座,c为目标底座),通过不断减少Num的个数最终到1,即可完成,那么不难发现,实际上这是个不断变换参数位置的递归,要注意输入参数的时候源底座和目标底座和临时底座所对应的变量是会变换的,借着这一思路,便有了接下来的算法
算法简介:
1,将num-1个盘子从源底座移到临时底座
2,将第Num个盘子从源底座移到目标底座
3,将临时底座的盘子转移到目标底座(将临时底座作为源底座,源底座作为临时底座,套用第一步过程)
对以上过程进行递归,得到结果
python代码:
可以显示每一步过程,并显示每一步过后各盘分布情况:
def swap_name(p):
t=p[0];
p[0]=p[1];
p[1]=t;
return p;
def hannoi(num,src,dst,temp=None): #num为盘子数量,src为源底座盘子列表,
#dst为目标底座盘子列表,temp为临时底座盘子列表
global src_name,temp_name,dst_name;
if(num==1):
po=src.pop();
print(po,"from",src_name,"->",dst_name);
dst.append(po);
print(src_name,src,temp_name,temp,dst_name,dst);
return;
temp_name,dst_name=swap_name([temp_name,dst_name]); #将temp作为目标底座,与目标底座交换盘名
hannoi(num-1,src,temp,dst);
temp_name,dst_name=swap_name([temp_name,dst_name]); #从上一函数出来后temp又作为临时底座,故将盘名交换回来
po=src.pop()
print(po,"from",src_name,"->",dst_name);
dst.append(po);
print(src_name,src,temp_name,temp,dst_name,dst);
src_name,temp_name=swap_name([src_name,temp_name]); #将temp作为源底座,故与源底座交换盘名
hannoi(num-1,temp,dst,src);
src_name,temp_name=swap_name([src_name,temp_name]); #交换回来
return;
def hannoi_i(num,src=[],dst=[],temp=[]): #num为盘子数量,src为源底座盘子列表,
#dst为目标底座盘子列表,temp为临时底座盘子列表
if(src==[]):
sr=[i for i in range(num)];
sr.reverse();
global src_name,temp_name,dst_name;
src_name="a";
temp_name="b";
dst_name="c";
hannoi(num,sr,dst,temp);
函数调用示例:
hannoi_i(3,[],[],[]) #3个盘子的经典汉诺塔问题,临时塔和目标塔不带任何盘子
结果:
输出每一步移动,以及移动过后各塔的盘子列表
其中,0代表最小的盘子,以此往上递增大小
2.如果不要求显示盘子列表以及盘子名称的话,可以将输入参数改为盘名,并删减过后,可有极其简洁的代码:
def hannota(num, i, j, k): #i,j,k为盘名,num为盘数
if (num == 1):
print(i, "->", k)
return
hannota(num - 1, i, k, j)
hannota(1, i, j, k)
hannota(num - 1, j, i, k)