C++非递归解决汉诺塔问题

汉诺塔问题简述:将塔A上的n个大小不一的盘子借由塔B全部移动到塔C上,且在过程中不能将大盘子放在小盘子上。

 

                 目录

1.算法:

2.具体效果:

3.SeqStack.cpp文件

4.Hanoi_main.cpp文件


汉诺塔问题虽然是经典的用递归方法求解的一个问题,但是对于一个看见递归就脑阔疼的人来说,还是想走走其他的路,因此我选择使用非递归来解决这个问题,具体如下:

算法:

不要问为什么可以这样做,问就是有一个美国学者研究表明 

具体效果:

 

PS:千万别把n设置的过大,输出不完的,因为要2^n-1次移动才能完成任务,当n=10的时候就已经要进行1023次移动了。 

详细代码如下:

SeqStack.cpp文件

主要任务:定义一个顺序栈,并且稍微修改其getTop()函数(与数据结构教材中定义的get函数一点点不同),设置当top指针指向-1时,调用getTop()函数则会返回999,避免塔已经空了还在移动空气到另外一个塔上。在后面的函数时会用到。

const int StackSize = 100;			//设置储存数组的大小为100
template <class DataType>
class SeqStack
{
public:
	SeqStack();						//初始化一个空栈,存储数组中什么都没有,令top=-1
	~SeqStack();					//不需要事实上,静态存储分配
	void Push(DataType x);			//入栈函数,令top++,收入x
	DataType Pop();					//出栈函数,送走栈顶元素,令top--;
	DataType GetTop();				//取栈顶元素,仅仅是取
	int Empty();					//判空函数
private:
	DataType data[StackSize];		//定义存储数组
	int top;						//令附设变量top为-1
};

template<class DataType>
SeqStack<DataType>::SeqStack(){
	top = -1;						//存储结构已经定义,在这里仅仅只需要令top为-1就行了
}

template<class DataType>
SeqStack<DataType>::~SeqStack(){
}

template<class DataType>
void SeqStack<DataType>::Push(DataType x)
{
	if (top == StackSize - 1)throw"入栈失败,栈已满";
	top++;
	data[top] = x;
}

template<class DataType>
DataType SeqStack<DataType>::Pop()
{
	DataType x;
	if (top == -1)throw "出栈失败,栈已空";
	x = data[top--];
	return x;
}

template<class DataType>
DataType SeqStack<DataType>::GetTop(){
	if (top != -1)return data[top];
	else return 999;						//设置底座为999,这样就不会发生从空底座取盘子的情况。
}

template<class DataType>
int
SeqStack<DataType>::Empty(){
	if (top == -1)return 1;
	else return 0;
}

Hanoi_main.cpp文件

主要任务:创建塔A,塔B,塔C,他们都是栈。然后将n个盘子放到A塔上,调用函数将n个盘子借由B塔移动到C塔上。

其中n为塔A的初始盘子数

i为移动的次数,每进行一次移动都会加一,当达到2^n-1次,即刻停止

/*
显然,汉诺塔问题符合栈的特点,后进先出;因此在这里我们要使用栈来解决
*/
#include <iostream>
#include <cmath>
#include "SeqStack.cpp"
using namespace std;

void putplate(int n, SeqStack<int> &A);    
//将n个盘子从大到小,低到高堆到塔A上的函数原型                        
int operate1inA(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为偶数且当最小的一号盘子在A塔时的处理方法
int operate1inB(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为偶数且当最小的一号盘子在B塔时的处理方法
int operate1inC(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为偶数且当最小的一号盘子在C塔时的处理方法
int operate1inOddA(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为奇数且当最小的一号盘子在A塔时的处理方法
int operate1inOddB(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为奇数且当最小的一号盘子在B塔时的处理方法
int operate1inOddC(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n);
//n为奇数且当最小的一号盘子在C塔时的处理方法

int main()
{
	int n;
	cout << "请输入宝塔A的盘子数" << endl;
	cin >> n;
	SeqStack<int>A;									//定义塔A
	SeqStack<int>B;									//定义塔B
	SeqStack<int>C;									//定义塔C
	putplate(n,A);									//将n个盘子从依次放到塔A上,从上到下序号逐渐变大
	try{
		if (n % 2 == 0){							//n为偶数
			for (int i = 0;;)			                //当盘子的个数为n时,移动的次数应等于2^n - 1  
			{
				if (A.GetTop() == 1){						//如果1号盘在A,移动到B
					if (operate1inA(A, B, C, i, n)){			//如果达到上限次数,终止循环
						break;
					}
				}
				else if (B.GetTop() == 1){					//在B则移动到C
					if (operate1inB(A, B, C, i, n)){					
						break;
					}
				}
				else if (C.GetTop() == 1){					//在C则移动到A
					if (operate1inC(A, B, C, i, n)){					
						break;
					}
				}
			}
		}
		else {
			for (int i = 0;;)			
			{
				if (A.GetTop() == 1){						//如果1号盘在A,移动到C
					if (operate1inOddA(A, B, C, i, n)){		//如果达到上限次数,终止循环
						break;
					}
				}
				else if (B.GetTop() == 1){				    //在B则移动到A
					if (operate1inOddB(A, B, C, i, n)){					
						break;
					}
				}
				else if (C.GetTop() == 1){					//在C则移动到B
					if (operate1inOddC(A, B, C, i, n)){					
						break;
					}
				}
			}
		}
	}
	catch (char *r){
		cout << r << endl;
	}
	system("pause");
	return 0;
}

void putplate(int n, SeqStack<int> &A){					//将n个盘子从依次放到塔A上,从上到下序号逐渐变大
	for (int i = n; i >= 1; i--){
		A.Push(i);										//i入栈
	}
	return;
}

int operate1inA(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	B.Push(1);										//将一号盘放入B塔
	A.Pop();
	i++;											//盘子的移动次数加一
	cout << "将1号盘子从A塔移动到B塔" << endl;
	if (i >= (pow(2, n) - 1)){						//如果移动次数达到了2^n-1,即刻停止
		return 1;                                        //1用于给if判断终止循环
	}
	if (C.GetTop() < A.GetTop()){					//将另外两个塔上的可以进行移动的盘子进行移动,
                                                    //事实上1号盘所在的塔不可能被放盘子,所以只要判断其他两个塔即可
		cout << "将" << C.GetTop() << "号盘子从C塔移动到A塔" << endl;
		A.Push(C.GetTop());							//如果C塔的最高盘子可以移动,则拿出C塔的最高盘子放入A
		C.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;									
		}
	}
	else if (A.GetTop() < C.GetTop()){
		cout << "将" << A.GetTop() << "号盘子从A塔移动到C塔" << endl;
		C.Push(A.GetTop());							//如果A塔的最高盘子可以移动,则拿出A塔的最高盘子放入C
		A.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	return 0;										//没有达到上限次数,不终止循环
}
int operate1inB(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	B.Pop();
	C.Push(1);
	i++;
	cout << "将1号盘子从B塔移动到C塔" << endl;
	if (i >= (pow(2, n) - 1)){						
		return 1;
	}
	if (B.GetTop() < A.GetTop()){				
		cout << "将" << B.GetTop() << "号盘子从B塔移动到A塔" << endl;
		A.Push(B.GetTop());							
		B.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	else if (A.GetTop() < B.GetTop()){
		cout << "将" << A.GetTop() << "号盘子从A塔移动到B塔" << endl;
		B.Push(A.GetTop());							
		A.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	return 0;
}
int operate1inC(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	C.Pop();
	A.Push(1);
	i++;
	cout << "将1号盘子从C塔移动到A塔" << endl;
	if (i >= (pow(2, n) - 1)){						
		return 1;
	}
	if (B.GetTop() < C.GetTop()){				
		cout << "将" << B.GetTop() << "号盘子从B塔移动到C塔" << endl;
		C.Push(B.GetTop());							
		B.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	else if (C.GetTop() < B.GetTop()){
		cout << "将" << C.GetTop() << "号盘子从C塔移动到B塔" << endl;
		B.Push(C.GetTop());							
		C.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	return 0;
}
int operate1inOddA(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	C.Push(1);										//将一号盘放入C塔
	A.Pop();
	i++;											//盘子的移动次数加一
	cout << "将1号盘子从A塔移动到C塔" << endl;
	if (i >= (pow(2, n) - 1)){						//如果移动次数达到了2^n-1,即刻停止
		return 1;                                        //1用于给if判断终止循环
	}
	if (B.GetTop() < A.GetTop()){					
		cout << "将" << B.GetTop() << "号盘子从B塔移动到A塔" << endl;
		A.Push(B.GetTop());							//如果C塔的最高盘子可以移动,则拿出C塔的最高盘子放入
		B.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){					//如果移动次数达到了2^n-1,即刻停止
			return 1;										
		}
	}
	else if (A.GetTop() < B.GetTop()){
		cout << "将" << A.GetTop() << "号盘子从A塔移动到B塔" << endl;
		B.Push(A.GetTop());								
		A.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	return 0;											
}
int operate1inOddC(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	C.Pop();
	B.Push(1);
	i++;
	cout << "将1号盘子从C塔移动到B塔" << endl;
	if (i >= (pow(2, n) - 1)){						
		return 1;
	}
	if (C.GetTop() < A.GetTop()){				
		cout << "将" << C.GetTop() << "号盘子从C塔移动到A塔" << endl;
		A.Push(C.GetTop());							
		C.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	else if (A.GetTop() < C.GetTop()){
		cout << "将" << A.GetTop() << "号盘子从A塔移动到C塔" << endl;
		C.Push(A.GetTop());							
		A.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){					
			return 1;
		}
	}
	return 0;
}
int operate1inOddB(SeqStack<int>&A, SeqStack<int>&B, SeqStack<int>&C, int &i, int n){
	B.Pop();
	A.Push(1);
	i++;
	cout << "将1号盘子从B塔移动到A塔" << endl;
	if (i >= (pow(2, n) - 1)){						
		return 1;
	}
	if (B.GetTop() < C.GetTop()){			
		cout << "将" << B.GetTop() << "号盘子从B塔移动到C塔" << endl;
		C.Push(B.GetTop());							
		B.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	else if (C.GetTop() < B.GetTop()){
		cout << "将" << C.GetTop() << "号盘子从C塔移动到B塔" << endl;
		B.Push(C.GetTop());							
		C.Pop();
		i++;
		if (i >= (pow(2, n) - 1)){						
			return 1;
		}
	}
	return 0;
}

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值