利用栈实现汉诺塔(Tower of Hanoi)

目录

前言

图解

代码解释


前言

在了解汉诺塔的规则之后,我们可以利用栈来实现汉诺塔。在这里,我使用了较为容易理解的递归法,如果有什么不足,欢迎指正。

递归法最重要的是要找到汉诺塔移动的规律,并不断细化。我们可以自己在在纸上或者在思维导图的软件上模拟一边流程,从而发现规律。

图解

如图,我们有3个柱子(栈),如何将1、2、3这三个圆盘从A移动到C呢?

想要将1、2、3移动到C上,最重要的一步是:先把“3”这一个方块移动到C的柱子上,然后,再对“3”上面的方块进行操作。但是想移动“3”,必须要移动“2、1”这两个方块, 必须要把“2、1”移动到B上,只有这样,方块“3“才能再A和C之间畅通无阻。因此,我们的问题就化简成如何实现下图:

接下来,我们的首要步骤是将1、2从A移动到B。

此时,细心的人也许会发现,我们最初的问题变得简单了:《从如何将1、2、3从A移动到C?》  简化为 《如何将1、2从A移动到B ?》。

同样的,想要将1、2从A移动到B,最重要的一步是:将“2”这个方块从A移动到B,然后再对“2”上方的方块进行操作,但是如何将方块“2”从A移动到B呢?答案是:必须将“2”上方的方块从A移动到C,即,将”1“从A移动到C。

 当我们实现如上图所示的情况时,”2“便可以从A到B畅通无阻,那么将”1“移动到”2“上之后,”3“便可以再A和C之间畅通无阻,然后我们就可以把”1、2“一起移动到”3“上(将"1、2"从B移动到C上)。

总结一下,汉诺塔问题可以用这样用递归解决:

要想将N个方块从A移动到C,就要

①将N-1个方块从A移动到B(利用递归解决这一步);

②将方块”N“从A移动到C(因为此时N上方的方块全在B柱子上,由于步骤①的实现,方块N可以再A和C之间畅通无阻);

③将剩余的N-1个方块从B移动到N的上方,也就是将N-1个方块从B移动到C(如何实现这一步?这一步不就是和步骤①一模一样吗?)。

这就是递归的算法解释,下面我们来看看代码部分。

代码解释

    /* 将 N 个方块从 栈a 移动到 栈b */
int Hanoi(int n, Stack* a, Stack* b, Stack* c)
{
	int i = 0;
	/*  当调用到n==1时,(调用到需要移动最上边的方块时)
	    我们便可以利用Pop()和Push()函数进行移动        */
	if (n == 1) {
		Pop(a, &i);
		Push(b, i);
		return 0;
	}

	/*第一步,将N-1个方块从 栈a移动到另一个栈, 栈c*/
	Hanoi(n - 1, a, c, b);//递归调用

	/*第二步,将栈a里仅有的一个方块从a移动到b*/
	Pop(a, &i);
	Push(b, i);

	/*第三步,将栈c中剩余的N-1个方块全部从c移动到b*/
	Hanoi(n - 1, c, b, a);	//递归调用

	return 0;
}

其中的Push(Stack* s, int elem)函数和Pop(Stakc* s, int* elem)函数为入栈和出栈函数。

再这里给出全部代码:

#include<stdio.h>
#include<stdlib.h>
#define ElemType int
#define Stack_Size 100
//顺序栈的表示和实现
typedef struct {
	ElemType elem[Stack_Size];
	int top;
}Stack;
void InitStack(Stack* s);
int Push(Stack* s, ElemType e);
int Pop(Stack* s, ElemType* e);
int Hanoi(int n,Stack* a, Stack* b, Stack* c);
void InPut(Stack* s,int n);
void Visit(Stack* s);
int main(void)
{
	Stack aa, bb, cc;
	Stack* a = &aa, * b = &bb, * c = &cc;
    //对栈aa、bb、cc进行初始化
	InitStack(a);//初始化时传递它们的指针
	InitStack(b);
	InitStack(c);

	int num;
	printf("Enter the number:");
	scanf("%d", &num);

    //输入函数,让用户在栈a中输入num个数字
	printf("Enter %d decrementing positive integers:\n",num);
	InPut(a, num);

	printf("\n\nEnter the anything to contiue:\n");
	(void)getchar();
	(void)getchar();
	Hanoi(num, a, c, b);

	printf("This is Stack A :\n");//接下来开始分别打印栈a、b、c的元素
	Visit(a);
	printf("This is Stack B :\n");
	Visit(b);
	printf("This is Stack C :\n");
	Visit(c);

	return 0;
}
void InitStack(Stack* s)
{
	s->top = -1;
}
int Push(Stack* s, ElemType e)
{
	if (s->top == Stack_Size - 1) {
		printf("Stack is full\n");
		return -1;
	}
	s->top++;
	s->elem[s->top] = e;
	return 0;
}
int Pop(Stack* s, ElemType* e)
{
	if (s->top == -1) {
		printf("Stack is empty\n");
		return -1;
	}
	*e = s->elem[s->top];
	s->top--;
	return 0;
}

/*
* 将 N 个方块从 栈a 移动到 栈b
*/
int Hanoi(int n, Stack* a, Stack* b, Stack* c)
{
	int i = 0;
	/*
	*当调用到n==1时,(调用到需要移动最上边的方块时)
	*我们便可以利用Pop()和Push()函数进行移动
	*/
	if (n == 1) {
		Pop(a, &i);
		Push(b, i);
		return 0;
	}

	/*第一步,将N-1个方块从 栈a移动到另一个栈, 栈c*/
	Hanoi(n - 1, a, c, b);//递归调用

	/*第二步,将栈a里仅有的一个方块从a移动到b*/
	Pop(a, &i);
	Push(b, i);

	/*第三步,将栈c中剩余的N-1个方块全部从c移动到b*/
	Hanoi(n - 1, c, b, a);	//递归调用

	return 0;
}
void Visit(Stack* s)
{
	int temp;
	while (!IsEmpty(s)) {
		Pop(s, &temp);
		printf("%d\n", temp);
	}printf("\n");
}
void InPut(Stack* s,int num)
{
	int ch;
	if (num == 0) {
		printf("The Stack A is :\n");
		return;
	}
	else {
		scanf_s("%d",&ch);
		Push(s, ch);InPut(s, num - 1);
		printf("%d\n", ch);
		
	}
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值