汉诺塔绝对是递归最经典的例子,具体的解释不多说了,直接上代码:
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
void move(int id, char x, char y)
{
printf("%d : %c -> %c\n", id, x, y);
}
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, A, C);
}
}
main()
{
hanoi(3, 'A', 'B', 'C');
}
输出:
1 : A -> C
2 : A -> B
1 : C -> B
3 : A -> C
1 : B -> A
2 : B -> C
1 : A -> C
这次的重头是汉诺塔的非递归实现,这个实现的算法思路是一个美国学者提出的(若有人知道这个美国学者的名字,欢迎在评论区指出)。这个算法出乎意料的简单,具体参考这个链接:汉诺塔杂谈(三)——非递归算法。此处就简单提一下过程:
首先将三根柱子按一定的规则摆成品字形:
若n为奇数,则品字形顺时针为ACB;若n为偶数,则品字形顺时针为ABC。
然后顺时针进行如下操作:
- 将当前柱子顶部的盘子移动到顺时针的下一个柱子。
- 将当前柱子和剩余的另一根柱子比较,将其中的一根柱子的盘子移动到另一根柱子上(很显然,这种移动方式是固定的)
- 指针指向顺时针的下一根柱子。
- 遇到某一根柱子的盘子满了时,停止循环。
代码写起来很简单,我们用三个栈来模拟三根柱子:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
using s=vector<int>;
int sum;
typedef struct
{
char name;
s 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 = 'A';
if (n % 2 == 1)
{
b.name = 'C';
c.name = 'B';
}
else
{
b.name = 'B';
c.name = 'C';
}
}
void move(stack &a, stack &b) // 将a中的栈顶元素移入b中
{
sum ++;
printf("%d : %c -> %c\n", a.stack.back(), a.name, 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);
}
}
cout << "共移动了" << sum << "步" << endl;
}
main()
{
stack A, B, C;
int n = 4;
initStack(A, B, C, n);
hanoi(n, A, B, C);
}
输出:
1 : A -> B
2 : A -> C
1 : B -> C
3 : A -> B
1 : C -> A
2 : C -> B
1 : A -> B
4 : A -> C
1 : B -> C
2 : B -> A
1 : C -> A
3 : B -> C
1 : A -> B
2 : A -> C
1 : B -> C
共移动了15步