一、问题背景
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。(来自百度百科)
二、任务描述
假设有A,B,C三座杆。在杆A上有n个直径各不相同、依次从小到大的编号为1,2.........,n的圆盘。现要求将A杆上的盘子全部移动到C杆上,并且要求:
1、每次只可以移动其中一根杆上的一只盘子。
2、每个盘子只能插在A,B,C其中一根杆上。
3、任何时刻任何杆上都不能出现较大盘子压在较小盘子上面的情况。
三、解决思路
该如何完成汉诺塔的任务呢?
当只有一个盘子时,即n=1。此时很简单,只要直接将A上的盘子移动到C上即可。
当n等于2时,则先要将第一个盘子移动到B上,再将第二个盘子移动到C上,最后将B上的盘子移动到C上。
依次类推,当n大于1的时候,可以考虑先将上面n-1个盘子通过A移动到B上,再将A上的最后一个盘子移动到C上,再将B上的n-1盘子通过A移动到C上。
这个显然是一个最适合递归的问题。
四、程序代码
1、算法一:简单粗暴型递归算法
# 递归法解hanoi塔问题
def move(n,fromSpike,toSpike):
global count,top
count+=1
print(f"第{count}回合:{n} 从{fromSpike} 移到 {toSpike}")
top[fromSpike]= '0' if str(n+1) in top.values() else str(n+1)
top[toSpike]=str(n)
def tower(n,fromSpike,spareSpike,toSpike):
if n==1:
move(1,fromSpike,toSpike)
else:
tower(n-1,fromSpike,toSpike,spareSpike)
move(n,fromSpike,toSpike)
tower(n-1,spareSpike,fromSpike,toSpike)
count = 0
top={'A':'1','B':'0','C':'0'}
tower(4,'A','B','C')
2、算法二:堆栈式迭代算法
# 迭代堆栈法解hanoi塔问题
class Task:
def __init__(self,num,top,fromSpike,toSpike,bufferSpike):
self.num = num
self.top = top
self.fromSpike = fromSpike
self.toSpike = toSpike
self.bufferSpike = bufferSpike
def __str__(self):
return f"{self.num} | {self.top} | {self.fromSpike} | {self.toSpike} | {self.bufferSpike}"
def move(self,count):
print(f"第{count}轮:{self.top}从{self.fromSpike} 移到 {self.toSpike}")
class Stack:
q=[]
def __init(self):
#self.q = []
pass
def push(self,o):
self.q.append(o)
#print("push:",self.q[-1])
return self
def pop(self):
#print("pop:",self.q[-1])
return self.q.pop()
def isEmpty(self):
return len(self.q)==0
def hanoi(num):
def push(n,i,f,t,b):
t = Task(n,i,f,t,b)
tasks.push(t)
tasks = Stack()
push(num,1,'A','C','B')
count =1
while not tasks.isEmpty():
task = tasks.pop()
if task.num==1:
task.move(count)
count+=1
else:
push(task.num-1,1,task.bufferSpike,task.toSpike,task.fromSpike)
push(1,task.num,task.fromSpike,task.toSpike,task.toSpike)
push(task.num-1,1,task.fromSpike,task.bufferSpike,task.toSpike)
hanoi(4)
五、总结
两段代码,运行结果都是一样的。从代码量看,递归算法的实现无疑是最简洁的,而堆栈式算法则相对要复杂一点;但是从执行效率看,则堆栈式迭代算法要高效很多,因为如果N很大,则递归算法需要很大规模的嵌套运算,每一层计算需要等待深一层嵌套计算结果的完成,这个是系统资源无法承受的。
堆栈式迭代算法仍然是递归算法的思路,不过这个版本的实现很好解决了每次移动哪个圆盘的问题,而且无需使用三个堆栈来记忆每个杆的圆盘状态。