这篇算是对汉诺塔的补充下篇再聊 ----全排列----递归
前言
上期讲了汉诺塔的递归实现,递归实现它有它的好处,但也有坏处
优点:
- 简洁。
- 在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。
缺点:
- 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。
- 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如斐波那契数列的递归实现。
- 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。
所以这期来讲讲非递归的实现,#补充#
需提前了解的知识
- 了解汉诺塔的递归实现——用非递归方法解决汉诺塔问题,这个问题的本质也就是将递归转化为非递归,所以原理和递归方法是一样的。所以搞懂非递归实现的前提是明白汉诺塔的递归实现
- 了解栈的基本原理及运用——熟悉出栈,入栈操作,先进后出原则
回顾
//实现问题的传递
//定义函数 hanoi(char a,char b,char c,int n);
//a,b,c 表示柱子,n表示有n个盘子
//a,b,c,n 表示n个盘子从a借助b移动到c
//现在有n个盘子,调用函数的表示也就是
hanoi(a,b,c,n); //n个盘子从a移动到c 那么传递给下一个的问题就应该是
hanoi(a,c,b,n-1); //将n-1个盘子从a,借助c,移动到b
cout<<a<<"->"<<c<<endl; //n-1个盘子移动到b后就可以将第n个盘子从a移动到c
hanoi(b,a,c,n-1); //此时,n-1个盘子在b上,那也就是要将n-1个盘借助a从b上移动到c
//问题的传递解决
如上代码,上期我们将 n 个盘子从 a 移动到 c 的问题分解为了下面三个部分的问题
- n-1 个盘子从 a 移动到 b
- 第 n 个盘子从 a 移动到 c
- n-1 个盘子从 b 移动到 c
程序顺序执行,也就是说这三部分的问题解决的顺序是 1 -> 2 -> 3,那么如果我们用栈来替代此时程序的执行,需要特别注意的一点是栈的规则是先进后出,也就是如果我们如果要将这三个问题分别压入栈中逐个解决,我们压栈的顺序就应该是 3 -> 2 -> 1。
所以,如果用栈来替代递归,入栈的顺序就应该是
- n-1 个盘子从 b 移动到 c 入栈
- 第 n 个盘子从 a 移动到 c 入栈
- n-1 个盘子从 a 移动到 b 入栈
如上图所示,n = 3 时先将 3 个盘子从 a 移动到 c 的问题入栈,再将问题提取出来,由于此时有 3 个盘子,问题无法解决,所以将问题分解,分解问题和递归是一样的,栈顶元素先解决或者分解,当只有一个盘子要移动时,只需要直接移动,问题的到解决,否则继续分解,直到只有一个盘子移动的问题时,将盘子移动过程输出,并将问题出栈。
#include<iostream>
using namespace std;
#define MaxSize 100
typedef struct han {
int n;
char a;
char b;
char c;
}han;
typedef struct stacknode{
han *stack;
int top;
}seqstack;
void pushstack(han e,seqstack &s) //入栈
{
if (s.top == MaxSize)
cout << "栈满" << endl;
else
s.stack[++s.top] = e;
}
han popstack(seqstack &s) //出栈
{
if (s.top == -1)
cout << "栈空" << endl;
else {
s.top--;
return s.stack[1 + s.top];
}
}
void hanoi(int n)
{
han sa, push;
sa.n = n; sa.a = 'A'; sa.b = 'B'; sa.c = 'C';
seqstack s;
s.stack = (han*)malloc(sizeof(han)*MaxSize);
s.top = -1;
pushstack(sa, s); //将问题入栈
while (s.top != -1) //栈空是结束循环##栈中存放未解决的问题,栈空则问题全部解决。
{
sa = popstack(s); //栈顶元素出栈 , 在下面解决或分解问题
if (sa.n == 1) //问题中只是一个盘子的移动时直接输出从 a 移动到 c 解决问题
cout << sa.a << "->" << sa.c << endl;
else { //分解问题
push.n = sa.n - 1;
push.a = sa.b; push.b = sa.a; push.c = sa.c;
pushstack(push, s); //问题 1 入栈
push.n = 1;
push.a = sa.a; push.b = sa.b; push.c = sa.c;
pushstack(push, s); //问题 2 入栈
push.n = sa.n - 1;
push.a = sa.a; push.b = sa.c; push.c = sa.b;
pushstack(push, s); //问题 3 入栈
}
}
}
int main()
{
int n;
cin >> n;
if (n <= 0)
return 0;
else
hanoi(n);
return 0;
}
代码如上,理解方法和递归实现一样
个人的见解,望大佬指正。