您的位置:首页 > 理论基础 > 数据结构算法

PTA 数据结构与算法 7-17 汉诺塔的非递归实现

2019-05-17 15:12 656 查看

如有不对,不吝赐教
进入正题:
借助堆栈以非递归(循环)方式求解汉诺塔的问题(n, a, b, c),即将N个盘子从起始柱(标记为“a”)通过借助柱(标记为“b”)移动到目标柱(标记为“c”),并保证每个移动符合汉诺塔问题的要求。

输入格式:
输入为一个正整数N,即起始柱上的盘数。

输出格式:
每个操作(移动)占一行,按柱1 -> 柱2的格式输出。

输入样例:
3

输出样例:
a -> c
a -> b
c -> b
a -> c
b -> a
b -> c
a -> c

这个题目要求我们用反常规的思维去思考汉诺塔问题,其实是对自己思维的一个锻炼,
这个题目的关键是用堆去实现函数递归中的栈调用
下面就以3个为例来分析
我们把从下到上的编号为3,2,1
以及栈的构造:

typedef struct Stack{
int num;            //柱子的编号
char source;        //源
char destination;   //目标
char mid;           //中间的柱子
struct Stack *down;
}stack;


下面我们将塔看为最底下的一个盘(即3)并上面的所有盘作为一个盘(即2和1),那么问题变为了两个盘,这个移动就很简单了。
我们先把1,2盘移动到b,再把3移动到c,最后再把1,2盘移动到c

这在栈里面的操作就是 Push 3:a->c
为了将3移动出来,我们要把1,2放到b,于是问题就变成了怎么把1,2放到b,按照上面的分发,栈中的操作就是Push 2:a->b,然后就变成了怎么把1放到c,执行Push:a->c
代码就是:

top=(stack *)malloc(sizeof(stack));
top->down=NULL;
top->source='a';
top->mid='b';
top->destination='c';
top->num=N;                  //初始化第一个栈节点

i=N-1;
while(i){
top=Push(i,top->source,top->destination,top->mid,top);
i--;
}

现在栈中的状态就是这样的:

然后我们要开始执行操作,Pop就相当于执行栈顶的操作
Pop 1:a->c后,stack和汉诺塔变成:

然后就是执行Pop 2:a->b,根据前面的分析,在执行这个操作后,1,2盘应该到了b上面但实际上是这样的:

所以我们还得在Pop完后在向其中把1盘放回b,即:Push 1:c->b
然后stack变成了这样:

这一步的操作就是这样的:

result=Pop(&top);
printf("%c -> %c\n",result->source,result->destination);
i=result->num-1;
if(!i)
continue;
stack *newOne;
newOne=(stack *)malloc(sizeof(stack));
newOne->num=i--;
newOne->source=result->mid;
newOne->mid=result->source;
newOne->destination=result->destination;
newOne->down=top;
top=newOne;
free(result);
while(i){
top=Push(i,top->source,top->destination,top->mid,top);
i--;
}
}

然后在进行Pop 1:c->b操作,就成了这样:

然后就是一样的操作,就可以把3放到c,1,2放到了b,于是问题就变成了把1,2从b放到c
问题就得以简化了
下面给出代码:

#include<stdio.h>
#include<malloc.h>

typedef struct Stack{
int num;            //柱子的编号
char source;        //源
char destination;   //目标
char mid;           //中间的柱子
struct Stack *down;
}stack;
stack *Push(int num,char source,char mid,char destination,stack *top);
stack *Pop(stack **top);

int main(void)
{
int N,i;
stack *top;
stack *result;
scanf("%d",&N);

top=(stack *)malloc(sizeof(stack));
top->down=NULL;
top->source='a';
top->mid='b';
top->destination='c';
top->num=N;                  //初始化第一个栈节点

i=N-1;
while(i){
top=Push(i,top->source,top->destination,top->mid,top);
i--;
}

while(top){
result=Pop(&top);
printf("%c -> %c\n",result->source,result->destination);
i=result->num-1;
if(!i)
continue;
stack *newOne;
newOne=(stack *)malloc(sizeof(stack));
newOne->num=i--;
newOne->source=result->mid;
newOne->mid=result->source;
newOne->destination=result->destination;
newOne->down=top;
top=newOne;
free(result);
while(i){
top=Push(i,top->source,top->destination,top->mid,top);
i--;
}
}

return 0;
}

stack *Push(int num,char source,char mid,char destination,stack *top)
{
stack *newOne;
newOne=(stack *)malloc(sizeof(stack));

newOne->num=num;
newOne->down=top;
newOne->source=source;                    //重置三个柱子
newOne->mid=mid;
newOne->destination=destination;
top=newOne;

return top;
}

stack *Pop(stack **top)
{
stack *mid;

mid=*top;
*top=(*top)->down;

return mid;
}

测试结果:

至于正确性,可以画图看下

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: