问题描述
在汉诺塔(Towers of Hanoi)问题中,假设有n个碟子和3座塔。初始时所有碟子从大到小堆在塔x上,我们要把所有碟子都移动到塔z上,每次移动一个,而且任何时候都不能把大碟子压在小碟子的上面。
求解策略
之前用数组的思想求解,之后又补上用栈的思想求解,源码放在数组方法源码的前面
递归。
为了把最大的碟子移到塔3的底部,必须把其余n-1个碟子移动到塔2,然后把最大的碟子移动到塔3。接下来是把塔2上的n-1个碟子移动到塔c。为此可以利用塔1和塔3。可以完全忽略塔3上的已有的一个碟子,因为我们已经把最大的碟子移到塔3,在它顶上可以堆放任何一个碟子。
下面用n=3时来模拟求解过程。
初始时:
我们想把三个碟子都移动到塔3,首先要把上面两个碟子看成一个整体,移动到塔2:
那么如何把上面两个碟子移动到塔3碟子的上面?首先要把塔2的第一个碟子移到塔1:
下面一步一步的模拟过程,以下是核心算法:
void Move(int id, TowerOfHanoi *a, TowerOfHanoi *b) {
//将编号为n的圆盘从塔盘a移至b
int i;
if (Find(id, a) == FALSE) {
printf("编号为%d的圆盘不在塔盘上\n", id);
return;
}
if (a->disk[0] != id) {
printf("编号为%d的圆盘不在塔顶,无法操作\n", id);
return;
}
for (i = 0; i < a->count - 1; i++) {
//移动塔盘a上的圆盘
a->disk[i] = a->disk[i + 1];
}
for (i = b->count - 1; i >= 0; i--) {
//移动塔盘b上的圆盘
b->disk[i + 1] = b->disk[i];
}
b->disk[0] = id;
a->count--;
b->count++;
Step++;//每移动一步,步数加一
}
void Hanoi(int n, TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int order) {
//将x塔座上编号为1到n的盘子移动到塔座z上,y为中转站
if (n == 1) {
Move(n, x, z);
PrintHanoi(x, y, z, order);
} else {
Hanoi(n - 1, x, z, y, order);
Move(n, x, z);
PrintHanoi(x, y, z, order);
Hanoi(n - 1, y, x, z, order);
}
}
第一步:
第二步:
第三步:
第四步:
第五步:
第六步:
第七步:
下面是源代码,求解n阶汉诺塔问题(C语言实现):
用堆栈的方法不会像用数组的方法可视化出来了,希望有大佬指点
栈:
源代码:
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;//表示函数返回状态
typedef int ElementType;//表示元素类型
typedef struct {
ElementType *top;//栈顶指针
ElementType *base;
ElementType stack_size;//当前已分配的存储空间,以元素为单位
char name;//记录塔盘名字
} StackHanoi;
static int Step = 0;//记录步数
Status CreatStackHanoi(StackHanoi *x, StackHanoi *y, StackHanoi *z, ElementType order);//初始化栈x,y,z
ElementType Length(StackHanoi s);//返回栈的元素个数
ElementType GetTop(StackHanoi s);//返回栈顶元素的值
Status Push(StackHanoi *s, ElementType t);//入栈操作,成功返回OK,否则返回ERROR
Status Pop(StackHanoi *s);//出栈,删除栈顶元素
Status Hanoi(ElementType n, ElementType order, StackHanoi *x, StackHanoi *y, StackHanoi *z);//将编号为1到n的圆盘从塔x移动到塔z,塔y做辅助塔
Status Move(StackHanoi *a, ElementType id, StackHanoi *b);//将编号为id的圆盘从塔a移动到塔b
void PrintHanoi(StackHanoi x, StackHanoi y, StackHanoi z);//输出汉诺塔x,y,z
Status Find(StackHanoi s, ElementType id);//查询编号为id的圆盘是否再塔盘s上,是返回TRUE,否则返回FALSE
int main() {
int order;//汉诺塔阶数
scanf("%d", &order);
StackHanoi x, y, z;//塔盘x,y,z
CreatStackHanoi(&x, &y, &z, order);
PrintHanoi(x, y, z);
Hanoi(order, order, &x, &y, &z);
return 0;
}
Status CreatStackHanoi(StackHanoi *x, StackHanoi *y, StackHanoi *z, ElementType order) {
//初始化栈x,y,z
x->base = (ElementType *) malloc(sizeof(ElementType) * (order + 1));
y->base = (ElementType *) malloc(sizeof(ElementType) * (order + 1));
z->base = (ElementType *) malloc(sizeof(ElementType) * (order + 1));
if (!x->base && !y->base && !z->base) return ERROR;
int i;
x->top = x->base;
y->top = y->base;
z->top = z->base;
x->stack_size = y->stack_size = z->stack_size = order;
x->name = 'X';
y->name = 'Y';
z->name = 'Z';
for (i = order; i > 0; i--) {
//将圆盘堆叠在塔盘x上
Push(x, i);
}
return OK;
}
ElementType Length(StackHanoi s) {
//返回栈的元素个数
return s.top - s.base;
}
ElementType GetTop(StackHanoi s) {
//返回栈顶元素的值
if (s.top == s.base) return ERROR;
return *(s.top - 1);
}
Status Push(StackHanoi *s, ElementType t) {
//入栈操作,成功返回OK,否则返回ERROR
if (s->top - s->base >= s->stack_size) {
//栈满,无法入栈
printf("塔盘空间已满,无法再移入盘子\n");
return ERROR;
}
*s->top = t;
s->top++;
return OK;
}
Status Pop(StackHanoi *s) {
//出栈,删除栈顶元素
if (s->top == s->base) {
//栈空,没有可删除的栈顶元素
printf("塔盘为空,没有可移走的圆盘\n");
return ERROR;
}
s->top--;
*s->top = -1;
return OK;
}
Status Hanoi(ElementType n, ElementType order, StackHanoi *x, StackHanoi *y, StackHanoi *z) {
//将编号为1到n的圆盘从塔x移动到塔z,塔y做辅助塔
if (n == 1) {
Move(x, n, z);
PrintHanoi(*x, *y, *z);
} else {
Hanoi(n - 1, order, x, z, y);
Move(x, n, z);
PrintHanoi(*x, *y, *z);
Hanoi(n - 1, order, y, x, z);
}
return OK;
}
Status Move(StackHanoi *a, ElementType id, StackHanoi *b) {
//将编号为id的圆盘从塔a移动到塔b}
if (Find(*a, id) == FALSE) {
printf("编号为%d的圆盘不在塔盘上\n", id);
return FALSE;
}
if (GetTop(*a) != id) {
printf("编号为%d的圆盘不在塔顶,无法移动\n", id);
return FALSE;
}
Push(b, id);
Pop(a);
Step++;
return TRUE;
}
Status Find(StackHanoi s, ElementType id) {
//查询编号为id的圆盘是否再塔盘s上,是返回TRUE,否则返回FALSE
int length = Length(s);
int i;
for (i = 0; i < length; i++) {
if (s.base[i] == id) {
return TRUE;
}
}
return FALSE;
}
void PrintHanoi(StackHanoi x, StackHanoi y, StackHanoi z) {
//输出汉诺塔x,y,z
printf("第%d步:\n", Step);
printf("%c塔盘上的圆盘编号:", x.name);
while (x.top != x.base) {
printf("%d\t", GetTop(x));
x.top--;
}
printf("\n%c塔盘上的圆盘编号:", y.name);
while (y.top != y.base) {
printf("%d\t", GetTop(y));
y.top--;
}
printf("\n%c塔盘上的圆盘编号:", z.name);
while (z.top != z.base) {
printf("%d\t", GetTop(z));
z.top--;
}
printf("\n");
}
输入n=3时,输出结果:
数组:
递归过程中因为传参的问题让输出汉诺塔的时候塔的相对位置发生变化,结果没有问题
源代码
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
typedef int ElementType;//表示元素类型
typedef int Status;//表示函数返回状态
typedef struct {
int count;//表示圆盘数量
int *disk;//圆盘数组,用 disk[0] 表示塔顶
} TowerOfHanoi;
int Step = 0;//记录步数
void CreatHanoi(TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int n);//初始化汉诺塔x,y,z,汉诺塔阶数为n
void Move(int id, TowerOfHanoi *a, TowerOfHanoi *b);//将编号为id的圆盘从塔盘a移至b
Status Find(int id, TowerOfHanoi *t);//检测编号为id的圆盘是否在塔盘t上
void Hanoi(int n, TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int order);//将x塔座上编号为1到n的盘子移动到塔座z上,y为中转站
void PrintHanoi(TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int order);//输出汉诺塔
int main() {
ElementType order;//存放汉诺塔阶数
scanf("%d", &order);
TowerOfHanoi x, y, z;
CreatHanoi(&x, &y, &z, order);
PrintHanoi(&x, &y, &z, order);
Hanoi(order, &x, &y, &z, order);
return 0;
}
void CreatHanoi(TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int n) {
//初始化汉诺塔,将盘子堆到x塔盘上
int i;
x->count = n;
y->count = z->count = 0;
//为塔盘分配空间
x->disk = (ElementType *) malloc(sizeof(int) * n);
y->disk = (ElementType *) malloc(sizeof(int) * n);
z->disk = (ElementType *) malloc(sizeof(int) * n);
for (i = 0; i < n; i++) {
x->disk[i] = i + 1;
y->disk[i] = 0;
z->disk[i] = 0;
}
}
Status Find(int id, TowerOfHanoi *t) {
//检测编号为id的圆盘是否在塔盘t上
int i;
for (i = 0; i < t->count; i++) {
if (t->disk[i] == id)
return TRUE;
}
return FALSE;
}
void Move(int id, TowerOfHanoi *a, TowerOfHanoi *b) {
//将编号为n的圆盘从塔盘a移至b
int i;
if (Find(id, a) == FALSE) {
printf("编号为%d的圆盘不在塔盘上\n", id);
return;
}
if (a->disk[0] != id) {
printf("编号为%d的圆盘不在塔顶,无法操作\n", id);
return;
}
for (i = 0; i < a->count - 1; i++) {
//移动塔盘a上的圆盘
a->disk[i] = a->disk[i + 1];
}
for (i = b->count - 1; i >= 0; i--) {
//移动塔盘b上的圆盘
b->disk[i + 1] = b->disk[i];
}
b->disk[0] = id;
a->count--;
b->count++;
Step++;//每移动一步,步数加一
}
void Hanoi(int n, TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int order) {
//将x塔座上编号为1到n的盘子移动到塔座z上,y为中转站
if (n == 1) {
Move(n, x, z);
PrintHanoi(x, y, z, order);
} else {
Hanoi(n - 1, x, z, y, order);
Move(n, x, z);
PrintHanoi(x, y, z, order);
Hanoi(n - 1, y, x, z, order);
}
}
void PrintHanoi(TowerOfHanoi *x, TowerOfHanoi *y, TowerOfHanoi *z, int order) {
int i, j;
printf("第%d步:\n", Step);
for (i = 0; i < order; i++) {
//控制行
for (j = 0; j < order * 3 + 2; j++) {
//控制列
if (order - x->count <= i && x->disk[i - (order - x->count)] > j) {//输出x
//判断能否打印并且打印的数量
printf("=");
} else {
printf(" ");
}
if (order - y->count <= i && y->disk[i - (order - y->count)] >= j - order && j - order > 0) {//输出y
//判断能否打印并且打印的数量
printf("=");
} else {
printf(" ");
}
if (order - z->count <= i && z->disk[i - (order - z->count)] >= j - 2 * order && j - 2 * order > 0) {//输出z
//判断能否打印并且打印的数量
printf("=");
} else {
printf(" ");
}
}
printf("\n");
}
}