线性结构实验 —— 堆栈、队列(汉诺塔的非递归实现)详细过程
线性结构实验 —— 堆栈、队列(汉诺塔的非递归实现)
一、 实验目的
- 熟练掌握堆栈、队列的两种存储结构实现方式及操作。
- 练习使用堆栈、队列结构解决问题的能力。
- 通过算法分析掌握不同存储结构的操作特点。
二、 实验内容和要求
问题描述
汉诺塔的非递归实现借助堆栈以非递归(循环)方式求解汉诺塔的问题(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
三、算法分析
1. 主流程设计
int main {
输入起始柱上的盘数;
通过借助柱移动盘子到目标柱;
输出盘子从起始柱通过借助柱移动到目标柱的详细步骤;
return 0;
}
2. ADT定义
struct Node //将栈内元素放入一个结构体中
{
char start; //起始柱
char mid; //借助柱
char goal; //目标柱
int num; //起始柱上的盘数
};
typedef int Position; //栈顶位置
typedef struct Node ElementType; //将堆栈的元素类型具体化
//typedef enum { false, true } bool;
/*堆栈的顺序表定义*/
typedef struct SNode* PtrToSNode;
struct SNode
{
ElementType* data; //存储元素的数组
Position Top; //堆栈的栈顶指针
int Maxsize; //堆栈的最大容量
};
typedef PtrToSNode Stack;
Stack CreateStack();
bool IsFull(Stack S);
bool push(Stack S, int n, char a, char b, char c);
bool IsEmpty(Stack S);
ElementType pop(Stack S);
3. 移动流程分析
设计思路:首先起始柱上除了底盘的其他盘子,需要通过目标柱移动到借助柱上,将此时的借助柱看作“目标柱”,将此时的目标柱看作“借助柱”压栈,栈满,不再执行下一次压栈(之后也是如此),然后将起始柱的底盘移动到目标柱上,最后将借助柱上的盘子通过起始柱移动到目标柱。
while(堆栈不为空)
{
弹出栈顶元素;
if (元素的num为1)
直接输出;
else
/*移动柱子相当于移动柱子上在最上面的盘子*/
当最底下的大盘到达目标柱,而其余的在借助柱时,压栈将借助柱和目标柱调换位置,达到盘子到达目标柱的目的;
当只剩下一个盘子没有到目标柱时,压栈将目标柱和起始柱调换位置,达到盘子到达目标柱的目的;
将盘子除了最底下的大盘移动到借助柱时,压栈将此时的借助柱作为目标柱,目标柱作为借助柱;
}
当操作数处理完毕后,运算符栈弹空;
4. 算法示例
当 n=3 即只有三个盘子时,如下为具体移动步骤图画展示:

下图为堆栈内的详细情况:

5. 算法示例(假设盘子的个数为3)

/*借助栈的非递归实现*/#include <stdio.h>
#include<stdlib.h>
struct Node //将栈内元素放入一个结构体中
{
char start; //起始柱
char mid; //借助柱
char goal; //目标柱
int num; //起始柱上的盘数
};
typedef int Position; //栈顶位置
typedef struct Node ElementType; //将堆栈的元素类型具体化
//typedef enum { false, true } bool;
/*堆栈的顺序表定义*/
typedef struct SNode* PtrToSNode;
struct SNode
{
ElementType* data; //存储元素的数组
Position Top; //堆栈的栈顶指针
int Maxsize; //堆栈的最大容量
};
typedef PtrToSNode Stack;
bool push(Stack S, int n, char a, char b, char c);
ElementType pop(Stack S);
/*
生成空堆栈,其最大长度为 Maxsize
*/
Stack CreateStack() {
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Maxsize = 1000;
S->data = (struct Node*)malloc(S->Maxsize * sizeof(struct Node));
S->Top = -1;
return S;
}
/*
判断堆栈S是否已满。
若S中元素个数等于 Maxsize 时返回 TRUE
否则返回 FALSE
*/
bool IsFull(Stack S) {
return (S->Top == S->Maxsize - 1);
}
/*
将元素压入堆栈。
若堆栈已满,返回 FALSE
否则将数据元素插入到堆栈 S 栈顶,返回 TRUE
*/
bool push(Stack S, ElementType X)
{
if (IsFull(S)) {
printf("Full Stack.\n");
return false;
}
else {
/*S->Top++;
S->data[S->Top].start = a;
S->data[S->Top].mid = b;
S->data[S->Top].goal = c;
S->data[S->Top].num = n;*/
S->data[++(S->Top)] = X;
return true;
}
}
/*
判断堆栈 S 是否为空。
若是返回 TRUE
否则返回 FALSE
*/
bool IsEmpty(Stack S) {
return (S->Top == -1);
}
/*
删除并返回栈顶元素。
若堆栈为空,返回错误信息
否则将栈顶数据元素从堆栈中删除并返回
*/
ElementType pop(Stack S)
{
if (IsEmpty(S)) {
printf("Empty Stack.\n");
ElementType e;
e.num = -1;
return e;
}
else
return S->data[S->Top--];
}
/*借助栈的非递归实现*/
int main()
{
int n;
scanf_s("%d", &n);
Stack S;
S = CreateStack();
struct Node m; //相当于ElementType m;
m.start = 'a';
m.mid = 'b';
m.goal = 'c';
m.num = n;
push(S, m); //将初始状态压栈
while (S->Top >= 0) //堆栈不空
{
m = pop(S); //弹出栈顶元素
if (m.num == 1)
{
printf("%c -> %c\n", m.start, m.goal);
}
else
{
/* n-1个盘,从辅助柱,借助源柱,移动到目标柱 */
//push(S, m.num - 1, m.mid, m.start, m.goal);
push(S, S->data[S->Top]);
S->data[S->Top].num = m.num - 1;
S->data[S->Top].start = m.mid;
S->data[S->Top].mid = m.start;
S->data[S->Top].goal = m.goal;
/* 最后一个盘,从源柱,借助辅助柱,移动到目标柱 */
//push(S, 1, m.start, m.mid, m.goal);
push(S, S->data[S->Top]);
S->data[S->Top].num = 1;
S->data[S->Top].start = m.start;
S->data[S->Top].mid = m.mid;
S->data[S->Top].goal = m.goal;
/* n-1个盘,从源柱,借助目标柱,移动到辅助柱 */
//push(S, m.num - 1, m.start, m.goal, m.mid);
push(S, S->data[S->Top]);
S->data[S->Top].num = m.num - 1;
S->data[S->Top].start = m.start;
S->data[S->Top].mid = m.goal;
S->data[S->Top].goal = m.mid;
}
}
return 0;
}
/*借助栈的递归实现*/
int main()
{
int n;
scanf_s("%d", &n);
Stack S;
S = CreateStack();
struct Node m;
push(S, n, 'a', 'b', 'c'); //将初始状态压栈
while (S->Top >= 0)
{
m = pop(S);
if (m.num == 1)
{
printf("%c -> %c\n", m.start, m.goal);
}
else
{
/* n-1个盘,从借助柱,借助起始柱,移动到目标柱 */
push(S, m.num - 1, m.mid, m.start, m.goal);
/* 最后一个盘,从起始柱,借助借助柱,移动到目标柱 */
push(S, 1, m.start, m.mid, m.goal);
/* n-1个盘,从起始柱,借助目标柱,移动到借助柱 */
push(S, m.num - 1, m.start, m.goal, m.mid);
}
}
return 0;
}
算法分析、堆栈分析图等均为原创作品,欢迎指正!

浙公网安备 33010602011771号