汉诺塔 --------- 非递归实现 ---- 栈的应用

这篇算是对汉诺塔的补充下篇再聊 ----全排列----递归

前言

上期讲了汉诺塔的递归实现,递归实现它有它的好处,但也有坏处
优点:

  1. 简洁。
  2. 在树的前序,中序,后序遍历算法中,递归的实现明显要比循环简单得多。

缺点:

  1. 递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。
  2. 递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如斐波那契数列的递归实现。
  3. 调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。

所以这期来讲讲非递归的实现,#补充#

需提前了解的知识

  1. 了解汉诺塔的递归实现——用非递归方法解决汉诺塔问题,这个问题的本质也就是将递归转化为非递归,所以原理和递归方法是一样的。所以搞懂非递归实现的前提是明白汉诺塔的递归实现
  2. 了解栈的基本原理及运用——熟悉出栈,入栈操作,先进后出原则

回顾

//实现问题的传递
//定义函数   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 的问题分解为了下面三个部分的问题

  1. n-1 个盘子从 a 移动到 b
  2. 第 n 个盘子从 a 移动到 c
  3. n-1 个盘子从 b 移动到 c

程序顺序执行,也就是说这三部分的问题解决的顺序是 1 -> 2 -> 3,那么如果我们用栈来替代此时程序的执行,需要特别注意的一点是栈的规则是先进后出,也就是如果我们如果要将这三个问题分别压入栈中逐个解决,我们压栈的顺序就应该是 3 -> 2 -> 1。

所以,如果用栈来替代递归,入栈的顺序就应该是

  1. n-1 个盘子从 b 移动到 c 入栈
  2. 第 n 个盘子从 a 移动到 c 入栈
  3. n-1 个盘子从 a 移动到 b 入栈
    汉诺塔 --------- 非递归实现 ---- 栈的应用_第1张图片
    如上图所示,n = 3 时先将 3 个盘子从 a 移动到 c 的问题入栈,再将问题提取出来,由于此时有 3 个盘子,问题无法解决,所以将问题分解,分解问题和递归是一样的,栈顶元素先解决或者分解,当只有一个盘子要移动时,只需要直接移动,问题的到解决,否则继续分解,直到只有一个盘子移动的问题时,将盘子移动过程输出,并将问题出栈。
#include
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;
}

代码如上,理解方法和递归实现一样

个人的见解,望大佬指正。

你可能感兴趣的:(算法,汉诺塔)