方法一
递归
汉诺塔问题的解决方案可以分为3步:
1、把n-1个盘子从left 借助 right,搬到mid柱子上;
2、把剩下最大的那一个盘子从left搬到right柱子上;
3、把n-1个盘子从mid 借助 left,搬到right柱子上。
示意图如下:
至于如何把n-1个盘子搬到另一个柱子上,同样参照上面的3步,不过此时柱子扮演的left,mid,right角色已经改变;对于n-2,n-3等等以此类推。
具体代码如下:
class Solution {
public:
vector<string> solution;
void Hanoi(int n, string left, string mid, string right){
if (n==0) return;
Hanoi(n-1,left,right,mid);//把n-1个盘子从left借助right搬到mid上去。
solution.push_back("move from " + left + " to " + right);//把第n个盘子从left搬到right上。
Hanoi(n-1,mid,left,right);//把n-1个盘子从mid借助left搬到right上去。
}
vector<string> getSolution(int n) {
Hanoi(n, "left", "mid", "right");
return solution;
}
};时间复杂度:O(2^N)。假设把n个盘子搬动耗时T(n),则有T(n)=2T(n-1)+1,从而[T(n)+1]/[T(n-1)+1]=2,对该等比数列求解即可。
空间复杂度:O(N)。递归栈的深度。
方法二
非递归算法
这种方法比较难想到。移动过程主要分为4个步骤:
1、将当前柱子顶部的盘子1移动到顺时针的下一个柱子。
2、将当前柱子和剩余的另一根柱子比较,将其中的一根柱子的盘子移动到另一根柱子上(很显然,这种移动方式是固定的)
3、指针指向顺时针的下一根柱子。
4、遇到某一根柱子的盘子满了时,停止循环;否则回到步骤1。
注意点在于当n为奇数时,顺时针的柱子顺序为left,right,mid;当n为偶数时,顺时针的柱子顺序为left,mid,right。
以n=3为例,示意图如下:
具体代码如下:
class Solution {
public:
vector<string> solution;
typedef struct
{
string name;
vector<int> stack;
}stack;
void initStack(stack &a, stack &b, stack &c, int n)
{
for (int i = n; i >= 1; -- i){
a.stack.push_back(i);
}
a.name = "left";
if (n % 2 == 1){
b.name = "right";
c.name = "mid";
}
else{
b.name = "mid";
c.name = "right";
}
}
void move(stack &a, stack &b) // 将a中的栈顶元素移入b中
{
solution.push_back("move from " + a.name + " to "+b.name);
b.stack.push_back(a.stack.back());
a.stack.pop_back();
}
void moveOneToOne(stack &a, stack &b) // 算法的第二步就是对于输入的两个栈,将其中一个栈的栈顶元素移到另一个栈中
{
// 任意两个不全为空的柱子的单次盘子移动方法是固定的
if (a.stack.empty()){ // a如果空的,自然只能把b的元素移到a中
move(b, a);
}
else if (b.stack.empty()){ // b如果空的,自然只能把a的元素移到b中
move(a, b);
}
else if (a.stack.back() > b.stack.back()){ // a的栈顶元素如果大于b的,自然只能把b的元素移到a中
move(b, a);
}
else{ // b的栈顶元素如果大于a的,自然只能把a的元素移到b中
move(a, b);
}
}
bool isEnd(stack &b, stack &c, int n) // 若有一根柱子满了,则循环结束
{
if (b.stack.size() == n || c.stack.size() == n){
return true;
}
else{
return false;
}
}
void hanoi(int n, stack &A, stack &B, stack &C)
{
for (int i = 0;!isEnd(B, C, n); ++ i){
if (i % 3 == 0){
move(A, B);
if (isEnd(B, C, n)){
break;
}
moveOneToOne(A, C);
}
else if (i % 3 == 1)
{
move(B, C);
if (isEnd(B, C, n)){
break;
}
moveOneToOne(B, A);
}
else{
move(C, A);
if (isEnd(B, C, n)){
break;
}
moveOneToOne(C, B);
}
}
}
vector<string> getSolution(int n) {
stack A,B,C;
initStack(A, B, C, n);
hanoi(n, A, B, C);
return solution;
}
};时间复杂度:O(2^N),因为总的移动次数没有减少,与方法一相比时间复杂度不会降低。
空间复杂度:O(N),开辟了3个数组用来存放盘子编号。

京公网安备 11010502036488号