汉诺塔(Hanoi)问题是学习递归算法时一个很经典的例子,通过递归算法解决,在C站上很多很多,今日就跟着鸡翅一起学习一下非递归算法吧!这次使用的是栈堆
的数据结构。
这里使用的非递归算法,本质上是模仿递归中大问题化为小问题解决的思想,使用栈堆来将大问题分解为几个按顺序解决的小问题。问:如何模仿?
答:
首先,举个例子,假设塔的数量为 n=3, 三根柱子分别命名为 a,b,c,要想全部搬到c柱,我们就必须先将最底下的盘子移动到c柱。所以我们遇到第一个的问题是,如何第3个(n)盘子从a柱借助b柱移动c柱,因为大不能压小,每次只能移动最上面的一个,所以解决这个问题的**基础(base)**应该是把上面两个(n-1)盘子从a借助c移动到b,这样我们就可以直接移动最地下的盘子,问题便得到解决。解决第一个问题之后,接下来的问题为将在b柱上的2(n-1)个盘子借助a柱移动到c柱子(next),要想解决这第二个问题,就必须将上面第一个盘子借助c柱移动到a柱,这样就可以直接移动最底下的盘子,以此类推,将每一个大问题分解为可以执行的小问题逐步解决。
先写一些模拟栈的基本运算函数以及构建栈
typedef struct{ //先定义栈元素
int n;
//n表示当前是第几个圆盘
char a,b,c;
// a,b,c代表三个柱子,a为当前圆盘所在柱子, b为可以借助的柱子, c为目标柱子,即要放到的那个位置的柱子
bool flag;
//代表此时圆盘可以移动不
}elem;
typedef struct {
int top; // top标记栈顶元素
elem e[64];
}stack;
这里有一点需要说明,这里的a,b,c柱子只需要认为是该盘子所在的柱子,可以借助的柱子以及目标柱子即可,这样可以方便分解问题。
// 栈定义函数 st为栈类型变量
void init(stack* &st)
{
st = (stack*) malloc (sizeof(stack));
st->top = -1;
}
// 栈销毁函数
void free_stack(stack* &st)
{
free(st);
}
// 出栈函数
void pop(stack* &st,elem &e)
{
e = st->e[st->top];
st->top--;
}
// 进栈函数
void push(stack* &st,elem &e)
{
st->top++;
st->e[st->top] = e;
}
// 判断是否为空栈
bool empty(stack* &st)
{
return st->top != -1;
}
这里是栈的基本运算函数,由于我懒 篇幅原因,这里不多做讲究,需要自行搜索其他博主的文章学习,此部分较为简单,还是很好学的。
void Hanoi(stack* &st, int n) //n为盘子的数量
{
init(st); //先初始化
elem e, e_next, e_finish, e_base;
//e为当前需要解决的问题,e_next为当前解决完之火需要解决的问题
//e_finish用来标记打印e问题已经完成, e_base是为了能直接解决e问题所需要解决的问题
e.a = 'a', e.b = 'b', e.c='c',e.n = n;
// 第一个需要解决的问题永远都是将第n个盘子由a柱子借助b柱子移动到c柱子
if(e.n == 1)
e.flag = true;
else
e.flag = false;
push(st,e); //将问题入栈
while(empty(st)) //当栈空就已经全部解决
{
pop(st,e); //解决栈顶的基础问题
if(e.flag == true) //此问题已经能够解决就打印出来,
printf("\t把第%d盘从%c放到%c上\n",e.n,e.a,e.c);
else //不能解决就分解问题
{
// 无法直接解决e问题,需要借助b柱子,将上面n-1个盘子移动到b柱
e_base.n = e.n -1;
e_base.a = e.a;
e_base.b = e.c;
e_base.c = e.b;
//因为移动到b柱子,如果无法直接解决,需要借助c柱子,所以这里b,c对调
if(e_base.n == 1)
e_base.flag = true;
else
e_base.flag = false;
// 解决base问题之后,第n个盘子上面已经没有盘子,e问题就能直接解决了,这里的flag直接为true
e_finish.n = e.n;
e_finish.a = e.a;
e_finish.b = e.b;
e_finish.c = e.c;
e_finish.flag = true;
e_next.n = e.n - 1;
e_next.a = e.b;
e_next.b = e.a;
// 因为e是无法直接解决的,需要借助b柱子解决,所以当e完成之后,第n-1个盘子是在b柱子,
//所以这里的a,b互相调换, 此时第n-1个盘子在b柱上
e_next.c = e.c;
if(e_next.n == 1) //如果是第一个就能直接解决
e_next.flag = true;
else
e_next.flag = false;
//因为栈先入后出的特性,这里入栈顺序颠倒
push(st,e_next);
push(st,e_finish);
push(st,e_base);
}
}
}
这里使用代码分解问题的步骤为:
#include
#include
typedef struct{ //先定义栈元素
int n;
//n表示当前是第几个圆盘
char a,b,c;
// a,b,c代表三个柱子,a为当前圆盘所在柱子, b为可以借助的柱子, c为目标柱子,即要放到的那个位置的柱子
bool flag;
//代表此时圆盘可以移动不
}elem;
typedef struct {
int top; // top标记栈顶元素
elem e[64];
}stack;
// 栈定义函数
void init(stack* &st)
{
st = (stack*) malloc (sizeof(stack));
st->top = -1;
}
// 栈销毁函数
void free_stack(stack* &st)
{
free(st);
}
// 出栈函数
void pop(stack* &st,elem &e)
{
e = st->e[st->top];
st->top--;
}
// 进栈函数
void push(stack* &st,elem &e)
{
st->top++;
st->e[st->top] = e;
}
// 判断是否为空栈
bool empty(stack* &st)
{
return st->top != -1;
}
void Hanoi(stack* &st, int n) //n为盘子的数量
{
init(st); //先初始化
elem e, e_next, e_finish, e_base;
//e为当前需要解决的问题,e_next为当前解决完之火需要解决的问题
//e_finish用来标记打印e问题已经完成, e_base是为了能直接解决e问题所需要解决的问题
e.a = 'a', e.b = 'b', e.c='c',e.n = n;
// 第一个需要解决的问题永远都是将第n个盘子由a柱子借助b柱子移动到c柱子
if(e.n == 1)
e.flag = true;
else
e.flag = false;
push(st,e); //将问题入栈
while(empty(st)) //当栈空就已经全部解决
{
pop(st,e); //解决栈顶的基础问题
if(e.flag == true) //此问题已经能够解决就打印出来,
printf("\t把第%d盘从%c放到%c上\n",e.n,e.a,e.c);
else //不能解决就分解问题
{
// 无法直接解决e问题,需要借助b柱子,将上面n-1个盘子移动到b柱
e_base.n = e.n -1;
e_base.a = e.a;
e_base.b = e.c;
e_base.c = e.b;
//因为移动到b柱子,如果无法直接解决,需要借助c柱子,所以这里b,c对调
if(e_base.n == 1)
e_base.flag = true;
else
e_base.flag = false;
// 解决base问题之后,第n个盘子上面已经没有盘子,e问题就能直接解决了,这里的flag直接为true
e_finish.n = e.n;
e_finish.a = e.a;
e_finish.b = e.b;
e_finish.c = e.c;
e_finish.flag = true;
e_next.n = e.n - 1;
e_next.a = e.b;
e_next.b = e.a;
// 因为e是无法直接解决的,需要借助b柱子解决,所以当e完成之后,第n-1个盘子是在b柱子,
//所以这里的a,b互相调换, 此时第n-1个盘子在b柱上
e_next.c = e.c;
if(e_next.n == 1) //如果是第一个就能直接解决
e_next.flag = true;
else
e_next.flag = false;
//因为栈先入后出的特性,这里入栈顺序颠倒
push(st,e_next);
push(st,e_finish);
push(st,e_base);
}
}
}
int main()
{
stack* st; //st为栈变量
int n=3; // n为数量
Hanoi(st,n);
return 0;
}
好像我已经很久没更新博客了,这次心血来潮,想分享一下在数据结构学习上的一些心得,本篇文章从构思到编写,花费的时间远远超过我学习这个思路,敲代码的时间。果然长时间不更还是有原因的,希望我这不成熟的文章能够帮助到你一点,天天开心!