C++ 数据结构学习 ---- 栈及其应用

目录

1.头文件

1.1 Stack.h 

1.2主文件头文件

2.栈的应用

2.1 进制转换器

2.2 进制转换器运行截图

 2.3 括号匹配玉逆波兰表达式

2.4 括号匹配与逆波兰表达式对应的函数

2.5 括号匹配与逆波兰表达式对应结果截图

2.6 N皇后问题

2.7 N皇后问题对应的函数

2.8 N皇后问题对应运行结果截图

2.9  迷宫寻径

2.10  迷宫寻径对应的函数

2.11  迷宫寻径对应的运行结果截图

 3.完整代码


1.头文件

1.1 Stack.h 

该头文件需要引入以下链接中的向量主体头文件C++ 数据结构学习 ---- 向量_孤城寻欢的博客-CSDN博客

#include "Vector.h"


template <typename T> class Stack : public Vector<T> {
public: //size()、empty()以及其他开放接口均可直接沿用
	void push(T const& e) { Vector<T>::insert(Vector<T>::size(), e); } //入栈
	T pop() { return Vector<T>::remove(Vector<T>::size() - 1); } //出栈
	T& top() { return (*this)[Vector<T>::size() - 1]; } //取顶
};//以向量为首/末端为栈底/顶

1.2主文件头文件

#include <iostream>
#include<stack>
#include <time.h>//引入随机数头文件

2.栈的应用

2.1 进制转换器

stack<char> q;
	static char digit[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	cout << "请输入要转换的十进制数:";
	int x; cin >> x;
	cout << "请输入要转换的进制(最高支持十六进制):";
	int y; cin >> y;
	if(y>16){
		cout << "您输入的进制太大了!"; 
		return 0;
	}
	while (x > 0)
	{
		q.push(digit[x%y]);
		x /=  y;
	}
	while (q.size())
	{
		cout << q.top() << " ";
		q.pop();
	}
	system("pause");
	return 0;

2.2 进制转换器运行截图

 2.3 括号匹配玉逆波兰表达式

//括号匹配
	cout << "请任意输入一个括号序列";
	string schar;
	cin >> schar;
	cout<<(bool)paren(schar);
	//计算括号表达式
	 char s[11] = {'0','+','1','*','(','5','!','-','2',')','\0'};
	 char o[6] = {'2','4','+','2','0','\0'};//24+20 ===44
	 cout << endl;
	 cout<< evaluate(s);
	 cout << endl;
	 cout<<evaluate(o);
	 cout << endl;
	
	 cout << "转换成逆波兰表达式:";
	 char rpn[11] = { 0 };
	 RPM(s, rpn);
	 for (int i = 0; i < 11; i++)
		cout << rpn[i] << " ";

2.4 括号匹配与逆波兰表达式对应的函数

//转换成逆波兰表达式,遇到数字是直接加入,操作符只有在出栈时才添加到串中
void RPM(char* s, char* rpm) {

	stack <float> opnd; stack<char> optr;
	optr.push('\0');
	int i = 0; int j = 0;
	while (!optr.empty())
	{
		if (isdigit(s[i])) {//判断是否是数字形式
			opnd.push(s[i] - '0');//把数字压入数字栈中
			rpm[j] = s[i];
			j++;
			i++;//向后移动下标
		}
		else {
			switch (orderBetween(optr.top(), s[i])) {//判断当前运算符与栈顶运算符的优先级
			case '<': {optr.push(s[i]); i++; }break;// <,压入到栈中等待处理 
			case '=':optr.pop(); i++; break;//遇到同级的即括号与哨兵,出栈
			case '>': {//遇到当前的运算符更低,栈顶运算符可以进行运算
				char op = optr.top();//获取当前的运算符,用来进行运算
				rpm[j] = op; j++;
				optr.pop(); //栈顶运算符出栈
				if (op == '!') {//一元运算符,阶乘调用计算函数
					float f1 = opnd.top(); opnd.pop();//一元运算只需要一个运算符
					opnd.push(calcu(op, f1));//将计算结果压入栈中
				}
				else {
					float f1 = opnd.top(); opnd.pop();
					float f2 = opnd.top(); opnd.pop();
					opnd.push(calcu(f1, op, f2));
				}
			}break;
			default:exit(-1);
			}
		}
	}
	opnd.pop();
}
//括号匹配
bool paren(string s) {
	int i = 0;
	stack<char> p;
	for (int i = 0; s[i] != '\0'; i++) {
		switch (s[i])
		{
		case '(':case '{':case'[': p.push(s[i]); break;
		case ')':if ((p.empty()) || ('(') != p.top()) return false; break;
		case ']':if ((p.empty()) || ('[') != p.top()) return false; break;
		case '}':if ((p.empty()) || ('{') != p.top()) return false; break;

		}
	}
	return p.empty();
}

2.5 括号匹配与逆波兰表达式对应结果截图

2.6 N皇后问题

//N皇后
	placeQueens(4);//输入4,答案是2
	cout << endl;

2.7 N皇后问题对应的函数

//解的总数
int nSolu = 0;
//尝试的总次数
int nCheck = 0; 
//皇后类
 struct Queen {
	 int x, y; //皇后在棋盘上的位置坐标
	 Queen(int xx = 0, int yy = 0) : x(xx), y(yy) {};
	 bool operator== (Queen const& q) const { //重载判等操作符,以检测不同皇后之间可能的冲突
	   return    (x == q.x) //行冲突(这一情况其实并不会发生,可省略)
	|| (y == q.y) //列冲突
	|| (x + y == q.x + q.y) //沿正对角线冲突
	|| (x - y == q.x - q.y); //沿反对角线冲突
	}
  bool operator!= (Queen const& q) const { return !(*this == q); } //重载不等操作符
};
//检查是否与栈中的皇后存在冲突
int checkout(stack<Queen> &solu, Queen& q)
{
	int num = 0;
	stack<Queen> temp(solu);
	//cout << "q.x:" << q.x << " q.y:" << q.y <<endl;
	while (temp.size())//从栈顶遍历至栈底
	{
		auto tm = temp.top(); temp.pop();
		if (q == tm)//当前皇后与栈顶皇后进行比较,若相等则发生冲突
			num++;//记录冲突的皇后个数
	}
	//cout << num << endl;
	return num;//返回冲突数
}
//打印皇后位置
void Print(stack<Queen>& solu) {
	stack<Queen> s = solu;
	int length = solu.size();
	
	while (length--) {
	std::cout << "x->" << s.top().x <<" " << "y->" << s.top().y << endl;
	s.pop();
	}

	cout << endl;
}
//N皇后算法(迭代版):采用试探/回溯的策略,借助栈记录查找的结果
 void placeQueens(int N) { 
	 stack<Queen> solu; //存放(部分)解的栈
	 Queen q(0, 0); //从原点位置出发
	 do { //反复试探、回溯
		 if (N <= solu.size() || N <= q.y) { //若已出界,则
			 q = solu.top(); solu.pop(); q.y++; //回溯一行,并继续试探下一列
		 }
		 else { //否则,试探下一行
			 while ((q.y < N) &&  checkout(solu,q)) //通过与已有皇后的比对
			 { q.y++; nCheck++; }//尝试找到可摆放下一皇后的列
			 if (N > q.y) { //若存在可摆放的列,则
				 solu.push(q); //摆上当前皇后,并
				 if (N == solu.size()) {
					 nSolu++; //记录解的个数
					 Print(solu);//打印结果
				 } //若部分解已成为全局解,则通过全局变量nSolu计数
				 q.x++; q.y = 0; //转入下一行,从第0列开始,试探下一皇后
			  }
		 }
	 } while ((0 < q.x) || (q.y < N)); //所有分支均已或穷举或剪枝之后,算法结束
	 std::cout <<N<<"皇后的解为" << nSolu << ",共尝试了" << nCheck<<"次!";
 }

2.8 N皇后问题对应运行结果截图

 

2.9  迷宫寻径

srand(int(time(0)));  //根据系统时间确定随机种子,保证每次执行都不同
	randLaby();//随机制造一个迷宫
	if (labyrinth(laby, startCell, goalCell))
		cout << "创造成功!" << endl;
	else
		cout << "创造失败!" << endl;
	displayLaby();//迷宫显示
	cout << "开始位置: "<<"○" << "(" << dec <<startCell->x << "," << dec<<startCell->y << ")"
		<< "  " << "终点位置: "<<"$"  << "(" << dec<<goalCell->x << "," << dec<<goalCell->y << ")" << endl;
	cout << "检查时间: " << dec <<ncheck << " 返回时间: " << dec <<nback << endl;
	cout << "路径的长度是 " << length << endl;

2.10  迷宫寻径对应的函数

//迷宫寻径 
 //现在所能得到的就是只能判断当前的迷宫是否能够找到出路,而最小路径由于概率不够随机的原因本人目前在DFS下暂时还没有实现

//迷宫寻径主流的三大算法:广度/深度优先搜素算法,以及A*算法
//相对而言,深度优先搜索是最适合迷宫出去路径寻径的,通过一步一步的试探和回溯,能很快找到一条出去的路
typedef enum { AVAILABLE, ROUTE, BACKTRACKED, WALL } Status;
typedef enum { UNKNOWN, EAST, SOUTH, WEST, NORTH, NO_WAY } ESWN;
inline ESWN nextESWN(ESWN eswn) { return ESWN(eswn + 1); }

static struct Cell
{
	int x, y; Status status;  //xy的坐标与类型
	ESWN incoming, outgoing;  //进入的方向与出去的方向
	Cell* prev;//前驱节点指针
};

#define LABY_MAX 40//宏定义
static Cell laby[LABY_MAX][LABY_MAX];//迷宫最大为40*40
static int ncheck, nback, length;
//移动的探测,即得到当前cell的邻居,根据outgoing确定方向
static inline Cell* neighbor(Cell* cell) 
{
	switch (cell->outgoing)
	{
	case EAST:return cell + LABY_MAX;
	case SOUTH:return cell + 1;
	case WEST:return cell - LABY_MAX;
	case NORTH:return cell - 1;
	default:exit(-1); //如果不是这四个方向,即UNKNOWN和NO_WAY,则直接退出这个switch循环
	}
}
//实质性的移动,根据cell的incoming移动当前cell到对应的cell
static inline Cell* advance(Cell* cell)  
{
	Cell* next;
	switch (cell->outgoing)
	{
	case EAST:next = cell + LABY_MAX; next->incoming = WEST; next->x = cell->x + 1; break;  //这里的操作意思是,现节点的进入为西,即相当于原节点的出是东
	case SOUTH:next = cell + 1;		  next->incoming = NORTH; next->y = cell->y + 1; break;
	case WEST:next = cell - LABY_MAX; next->incoming = EAST; next->x = cell->x - 1; break;
	case NORTH:next = cell - 1;		  next->incoming = SOUTH; next->y = cell->y - 1; break;
	default: exit(-1);
	}
	return next;
}
// 迷宫寻径算法:在格单元s至t之间规划一条通路(如果的确存在)
static bool labyrinth(Cell Laby[LABY_MAX][LABY_MAX], Cell* s, Cell* t)
{
	if ((AVAILABLE != s->status) || (AVAILABLE != t->status)) return false;  //首先,起点和终点必须是能访问的
	stack<Cell*> path;  //栈中存放的都是指向cell单元的指针,这样对于栈的操作过程都是指针操作,能有效提升效率
	s->incoming = UNKNOWN; s->status = ROUTE; path.push(s);  //将起点的进入点设为无,然后状态设为在路径上,最后入栈
	do
	{
		Cell* c = path.top();  //c是指向栈顶元素的指针,用于处理当前栈顶的节点数据
		if (c == t)
		{
			length = path.size();
			return true;  //迷宫的最终条件,找到终点
		}
		while (NO_WAY > (c->outgoing = nextESWN(c->outgoing)))  //将c的出方向改为nextESWN枚举中的下个元素(未知,东南西北,无路)
			if (AVAILABLE == neighbor(c)->status) break;		//遍历c的各个邻居(东南西北方向),一旦有可行的就跳出,不然就循环
																//注意上面的循环终止条件,要么是邻居可走就跳出,要么就是走到了NO_WAY,也就是无路可走,所以跳出
																//同时注意,这里是while循环,回溯之后的cell过此段代码时,会先nextESWN到下一个方向,不会出现一个方向无限循环的情况
																//这里有个很有意思的想法,既然在检查方向,其肯定会检查到其incoming的方向,但是前面可以看到,只要走过的路都会标成ROUTE,所以不会干涉
		if (NO_WAY <= c->outgoing)  //说穿了,就是无路可走了,如同字面意思
		{
			c->status = BACKTRACKED;  //将当前的节点c,即对应的栈顶元素标记为BACKTRACKED,即已经走过但是试探全部失败回溯的点,类似于忒休斯的标志
			path.pop();  //栈顶元素出栈,但是cell c本质上还是存在的,没有删除。从实质上实现回溯
			nback++;
		}
		else
		{
			path.push(c = advance(c));   //将c根据前面试探可行的方向移动之后,将移动后的c入栈(此时的C已经是一个新的cell指针了,没有指向之前的栈顶元素了
			c->outgoing = UNKNOWN;  //新的c的出方向必然为未知
			c->status = ROUTE; //新的栈顶元素的标志改为ROUTE,表示进入路径试探了
			ncheck++;
		}
	} while (!path.empty());  //直到存储路径的path为空
	length = path.size();
	return false;  //如果循环内没有实现true的返回,代表起点到终点没有路,那么最终只能返回false了
}

//输出某一迷宫格的信息
static void printLabyCell(Cell* elem)
{
	//printf("%d -> (%d, %d) -> %d\n",((Cell*)elem)->incoming,((Cell*)elem)->x,((Cell*)elem)->y,((Cell*)elem)->outgoing);
	cout <<  elem->incoming << "->("<<elem->x<<","<<elem->y<<")"<<"->"<<elem->outgoing;
}
//此处借用dascpp中邓公的随机迷宫生成程序
static int labySize;  
static Cell* startCell;
static Cell* goalCell;
//随机迷宫
static void randLaby()
{
	labySize = LABY_MAX / 2 + rand() % (LABY_MAX / 2); //生成一个随机size的迷宫
	cout << "一个大小为"<<labySize<<"的迷宫,";
	//printf("Using a laby of size %d ...\n", labySize);
	for (int i = 0; i < labySize; i++)
		for (int j = 0; j < labySize; j++)
		{
			laby[i][j].x = i;
			laby[i][j].y = j;
			laby[i][j].incoming =laby[i][j].outgoing = UNKNOWN;
			laby[i][j].status = WALL; //边界格点必须是墙
		}
	for (int i = 1; i < labySize - 1; i++)
		for (int j = 1; j < labySize - 1; j++)
			if (rand() % 4) laby[i][j].status = AVAILABLE; //75%的格点为空可用
	startCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
	goalCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
	startCell->status = goalCell->status = AVAILABLE; //起始格点必须可用
}

//迷宫显示
static void displayLaby() { //┘└┐┌│─
	

	// char  pattern[5][5] =
	//{
	//	'┼', '┼', '┼', '┼', '┼',
	//	'┼', '  ', '┌', '─', '└',
	//	'┼', '┌', '  ', '┐', '│',
	//	'┼', '─', '┐', '  ', '┘',
	//	'┼', '└', '│', '┘', '  '
	//};
	 const char*  pattern[5][5] =
	{
		   "┼", "┼", "┼", "┼", "┼",
			"┼", "  ", "┌", "─", "└",
			"┼", "┌", "  ", "┐", "│",
			"┼", "─", "┐", "  ", "┘",
			"┼", "└", "│", "┘", "  "
	};

	//system("cls");//清屏操作
	 cout << " ";
	 for (int j = 0; j < labySize; j++)//输出迷宫的列号
		if (j < 10)
		 cout << hex << j;
		else cout << (char)('A' + j - 10);
		//(j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
	cout << endl;
	for (int j = 0; j < labySize; j++)//输出迷宫的行号
	{
		if (j < 10)
			cout << hex << j;
		else cout<< (char)('A'+j-10);
		//(j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
		for (int i = 0; i < labySize; i++)
			if (startCell == &laby[i][j])
				cout << "○";//出生点打印出○
			else if (goalCell == &laby[i][j])
				cout << " $";//终点打印$
			else
				switch (laby[i][j].status)
				{
					
				case WALL:  cout << "█";//如果是墙打印█
					//printf("█"); 
					break;
				case BACKTRACKED: cout << "*";//回溯过的打印*
					//printf("*");  
					break;
				case AVAILABLE: cout << " ";//可以同行的打印空
					// printf("  ");  
					break;
				default:
					cout << pattern [laby[i][j].outgoing][laby[i][j].incoming]<<" ";
					break;
				}
		cout << endl;
	}
}

2.11  迷宫寻径对应的运行结果截图

 3.完整代码

#include <iostream>
#include<stack>
#include <time.h>//引入随机数头文件
using namespace std;


#define N_OPTR 9 //运算符总数
typedef enum { ADD, SUB, MUL, DIV, POW, FAC, L_P, R_P, EOE } Operator; //运算符集合
//加、减、乘、除、乘方、阶乘、左括号、右括号、起始符与终止符
const char pri[N_OPTR][N_OPTR] =   //运算符优先等级 [栈顶] [当前]
{
	//  |-------------------- 当 前 运 算 符 --------------------| 
//              +      -      *      /      ^      !      (      )      \0 
//  +    
			   '>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
			   // |      
						'>',   '>',   '<',   '<',   '<',   '<',   '<',   '>',   '>',
			 // 栈  *  
							 '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
				 // 顶  /   
							 '>',   '>',   '>',   '>',   '<',   '<',   '<',   '>',   '>',
				 // 运  ^    
					  '>',   '>',   '>',   '>',   '>',   '<',   '<',   '>',   '>',
			// 算  !  
						   '>',   '>',   '>',   '>',   '>',   '>',   ' ',   '>',   '>',
				   // 符  (
				   '<',   '<',   '<',   '<',   '<',   '<',   '<',   '=',   ' ',
	  // |   )
				 ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',   ' ',
// -- \0     
				'<',   '<',   '<',   '<',   '<',   '<',   '<',   ' ',   '='
};
//由运算符转译出编号
Operator optr2rank(char op)
{
	switch (op)
	{
	case '+': return ADD; //加
	case '-': return SUB; //减
	case '*': return MUL; //乘
	case '/': return DIV; //除
	case '^': return POW; //乘方
	case '!': return FAC; //阶乘
	case '(': return L_P; //左括号
	case ')': return R_P; //右括号
	case '\0':return EOE; //起始符与终止符
//	default: exit(-1); //未知运算符
	}
}
//阶乘运算
float calcu(char s, float f1) {
	if (f1 == 0 || f1 == 1) return 1;
	float sum = 1;
	for (int i = 1; i <= f1; i++) {
		sum *= i;
	}
	return sum;
}
//二目运算符
float calcu(float f1, char s, float f2) {
	if (s == '+') return f1 + f2;
	if (s == '-') return f2 - f1;
	if (s == '*') return f1 * f2;
	if (s == '/') return f2 / f1;
	if (s == '^') {
		float temp = f1;
		for (int i = 0; i < f2; i++) {
			f1 *= temp;
		}
		return f1;
	}
	return 0;
}
//比较两个运算符之间的优先级
char orderBetween(char op1, char op2)
{
	return pri[optr2rank(op1)][optr2rank(op2)];
}
//对表达式s求值,
float evaluate(char* s)
{
	stack <float> opnd; stack<char> optr;
	optr.push('\0');
	int i = 0;
	while (!optr.empty())
	{
		if (isdigit(s[i])) {//判断是否是数字形式
			if (i != 0 && isdigit(s[i - 1])) {//解决多位数
				float f = opnd.top(); opnd.pop();
				opnd.push(f * 10 + s[i] - '0');
			}
			else
				opnd.push(s[i] - '0');//把数字压入数字栈中

			i++;//向后移动下标
		}
		else {
			switch (orderBetween(optr.top(), s[i])) {//判断当前运算符与栈顶运算符的优先级
			case '<': {optr.push(s[i]); i++; }break;// <,压入到栈中等待处理 
			case '=':optr.pop(); i++; break;//遇到同级的即括号与哨兵,出栈
			case '>': {//遇到当前的运算符更低,栈顶运算符可以进行运算
				char op = optr.top();//获取当前的运算符,用来进行运算
				optr.pop(); //栈顶运算符出栈
				if (op == '!') {//一元运算符,阶乘调用计算函数
					float f1 = opnd.top(); opnd.pop();//一元运算只需要一个运算符
					opnd.push(calcu(op, f1));//将计算结果压入栈中
				}
				else {
					float f1 = opnd.top(); opnd.pop();
					float f2 = opnd.top(); opnd.pop();
					opnd.push(calcu(f1, op, f2));
				}
			}break;
			default:exit(-1);//语法错误直接退出
			}
		}
	}
	return opnd.top();
}
//转换成逆波兰表达式,遇到数字是直接加入,操作符只有在出栈时才添加到串中
void RPM(char* s, char* rpm) {

	stack <float> opnd; stack<char> optr;
	optr.push('\0');
	int i = 0; int j = 0;
	while (!optr.empty())
	{
		if (isdigit(s[i])) {//判断是否是数字形式
			opnd.push(s[i] - '0');//把数字压入数字栈中
			rpm[j] = s[i];
			j++;
			i++;//向后移动下标
		}
		else {
			switch (orderBetween(optr.top(), s[i])) {//判断当前运算符与栈顶运算符的优先级
			case '<': {optr.push(s[i]); i++; }break;// <,压入到栈中等待处理 
			case '=':optr.pop(); i++; break;//遇到同级的即括号与哨兵,出栈
			case '>': {//遇到当前的运算符更低,栈顶运算符可以进行运算
				char op = optr.top();//获取当前的运算符,用来进行运算
				rpm[j] = op; j++;
				optr.pop(); //栈顶运算符出栈
				if (op == '!') {//一元运算符,阶乘调用计算函数
					float f1 = opnd.top(); opnd.pop();//一元运算只需要一个运算符
					opnd.push(calcu(op, f1));//将计算结果压入栈中
				}
				else {
					float f1 = opnd.top(); opnd.pop();
					float f2 = opnd.top(); opnd.pop();
					opnd.push(calcu(f1, op, f2));
				}
			}break;
			default:exit(-1);
			}
		}
	}
	opnd.pop();
}
//括号匹配
bool paren(string s) {
	int i = 0;
	stack<char> p;
	for (int i = 0; s[i] != '\0'; i++) {
		switch (s[i])
		{
		case '(':case '{':case'[': p.push(s[i]); break;
		case ')':if ((p.empty()) || ('(') != p.top()) return false; break;
		case ']':if ((p.empty()) || ('[') != p.top()) return false; break;
		case '}':if ((p.empty()) || ('{') != p.top()) return false; break;

		}
	}
	return p.empty();
}


//解的总数
int nSolu = 0;
//尝试的总次数
int nCheck = 0; 
//皇后类
 struct Queen {
	 int x, y; //皇后在棋盘上的位置坐标
	 Queen(int xx = 0, int yy = 0) : x(xx), y(yy) {};
	 bool operator== (Queen const& q) const { //重载判等操作符,以检测不同皇后之间可能的冲突
	   return    (x == q.x) //行冲突(这一情况其实并不会发生,可省略)
	|| (y == q.y) //列冲突
	|| (x + y == q.x + q.y) //沿正对角线冲突
	|| (x - y == q.x - q.y); //沿反对角线冲突
	}
  bool operator!= (Queen const& q) const { return !(*this == q); } //重载不等操作符
};
//检查是否与栈中的皇后存在冲突
int checkout(stack<Queen> &solu, Queen& q)
{
	int num = 0;
	stack<Queen> temp(solu);
	//cout << "q.x:" << q.x << " q.y:" << q.y <<endl;
	while (temp.size())//从栈顶遍历至栈底
	{
		auto tm = temp.top(); temp.pop();
		if (q == tm)//当前皇后与栈顶皇后进行比较,若相等则发生冲突
			num++;//记录冲突的皇后个数
	}
	//cout << num << endl;
	return num;//返回冲突数
}
//打印皇后位置
void Print(stack<Queen>& solu) {
	stack<Queen> s = solu;
	int length = solu.size();
	
	while (length--) {
	std::cout << "x->" << s.top().x <<" " << "y->" << s.top().y << endl;
	s.pop();
	}

	cout << endl;
}
//N皇后算法(迭代版):采用试探/回溯的策略,借助栈记录查找的结果
 void placeQueens(int N) { 
	 stack<Queen> solu; //存放(部分)解的栈
	 Queen q(0, 0); //从原点位置出发
	 do { //反复试探、回溯
		 if (N <= solu.size() || N <= q.y) { //若已出界,则
			 q = solu.top(); solu.pop(); q.y++; //回溯一行,并继续试探下一列
		 }
		 else { //否则,试探下一行
			 while ((q.y < N) &&  checkout(solu,q)) //通过与已有皇后的比对
			 { q.y++; nCheck++; }//尝试找到可摆放下一皇后的列
			 if (N > q.y) { //若存在可摆放的列,则
				 solu.push(q); //摆上当前皇后,并
				 if (N == solu.size()) {
					 nSolu++; //记录解的个数
					 Print(solu);//打印结果
				 } //若部分解已成为全局解,则通过全局变量nSolu计数
				 q.x++; q.y = 0; //转入下一行,从第0列开始,试探下一皇后
			  }
		 }
	 } while ((0 < q.x) || (q.y < N)); //所有分支均已或穷举或剪枝之后,算法结束
	 std::cout <<N<<"皇后的解为" << nSolu << ",共尝试了" << nCheck<<"次!";
 }


 //迷宫寻径 
 //现在所能得到的就是只能判断当前的迷宫是否能够找到出路,而最小路径由于概率不够随机的原因本人目前在DFS下暂时还没有实现

//迷宫寻径主流的三大算法:广度/深度优先搜素算法,以及A*算法
//相对而言,深度优先搜索是最适合迷宫出去路径寻径的,通过一步一步的试探和回溯,能很快找到一条出去的路
typedef enum { AVAILABLE, ROUTE, BACKTRACKED, WALL } Status;
typedef enum { UNKNOWN, EAST, SOUTH, WEST, NORTH, NO_WAY } ESWN;
inline ESWN nextESWN(ESWN eswn) { return ESWN(eswn + 1); }

static struct Cell
{
	int x, y; Status status;  //xy的坐标与类型
	ESWN incoming, outgoing;  //进入的方向与出去的方向
	Cell* prev;//前驱节点指针
};

#define LABY_MAX 40//宏定义
static Cell laby[LABY_MAX][LABY_MAX];//迷宫最大为40*40
static int ncheck, nback, length;
//移动的探测,即得到当前cell的邻居,根据outgoing确定方向
static inline Cell* neighbor(Cell* cell) 
{
	switch (cell->outgoing)
	{
	case EAST:return cell + LABY_MAX;
	case SOUTH:return cell + 1;
	case WEST:return cell - LABY_MAX;
	case NORTH:return cell - 1;
	default:exit(-1); //如果不是这四个方向,即UNKNOWN和NO_WAY,则直接退出这个switch循环
	}
}
//实质性的移动,根据cell的incoming移动当前cell到对应的cell
static inline Cell* advance(Cell* cell)  
{
	Cell* next;
	switch (cell->outgoing)
	{
	case EAST:next = cell + LABY_MAX; next->incoming = WEST; next->x = cell->x + 1; break;  //这里的操作意思是,现节点的进入为西,即相当于原节点的出是东
	case SOUTH:next = cell + 1;		  next->incoming = NORTH; next->y = cell->y + 1; break;
	case WEST:next = cell - LABY_MAX; next->incoming = EAST; next->x = cell->x - 1; break;
	case NORTH:next = cell - 1;		  next->incoming = SOUTH; next->y = cell->y - 1; break;
	default: exit(-1);
	}
	return next;
}
// 迷宫寻径算法:在格单元s至t之间规划一条通路(如果的确存在)
static bool labyrinth(Cell Laby[LABY_MAX][LABY_MAX], Cell* s, Cell* t)
{
	if ((AVAILABLE != s->status) || (AVAILABLE != t->status)) return false;  //首先,起点和终点必须是能访问的
	stack<Cell*> path;  //栈中存放的都是指向cell单元的指针,这样对于栈的操作过程都是指针操作,能有效提升效率
	s->incoming = UNKNOWN; s->status = ROUTE; path.push(s);  //将起点的进入点设为无,然后状态设为在路径上,最后入栈
	do
	{
		Cell* c = path.top();  //c是指向栈顶元素的指针,用于处理当前栈顶的节点数据
		if (c == t)
		{
			length = path.size();
			return true;  //迷宫的最终条件,找到终点
		}
		while (NO_WAY > (c->outgoing = nextESWN(c->outgoing)))  //将c的出方向改为nextESWN枚举中的下个元素(未知,东南西北,无路)
			if (AVAILABLE == neighbor(c)->status) break;		//遍历c的各个邻居(东南西北方向),一旦有可行的就跳出,不然就循环
																//注意上面的循环终止条件,要么是邻居可走就跳出,要么就是走到了NO_WAY,也就是无路可走,所以跳出
																//同时注意,这里是while循环,回溯之后的cell过此段代码时,会先nextESWN到下一个方向,不会出现一个方向无限循环的情况
																//这里有个很有意思的想法,既然在检查方向,其肯定会检查到其incoming的方向,但是前面可以看到,只要走过的路都会标成ROUTE,所以不会干涉
		if (NO_WAY <= c->outgoing)  //说穿了,就是无路可走了,如同字面意思
		{
			c->status = BACKTRACKED;  //将当前的节点c,即对应的栈顶元素标记为BACKTRACKED,即已经走过但是试探全部失败回溯的点,类似于忒休斯的标志
			path.pop();  //栈顶元素出栈,但是cell c本质上还是存在的,没有删除。从实质上实现回溯
			nback++;
		}
		else
		{
			path.push(c = advance(c));   //将c根据前面试探可行的方向移动之后,将移动后的c入栈(此时的C已经是一个新的cell指针了,没有指向之前的栈顶元素了
			c->outgoing = UNKNOWN;  //新的c的出方向必然为未知
			c->status = ROUTE; //新的栈顶元素的标志改为ROUTE,表示进入路径试探了
			ncheck++;
		}
	} while (!path.empty());  //直到存储路径的path为空
	length = path.size();
	return false;  //如果循环内没有实现true的返回,代表起点到终点没有路,那么最终只能返回false了
}

//输出某一迷宫格的信息
static void printLabyCell(Cell* elem)
{
	//printf("%d -> (%d, %d) -> %d\n",((Cell*)elem)->incoming,((Cell*)elem)->x,((Cell*)elem)->y,((Cell*)elem)->outgoing);
	cout <<  elem->incoming << "->("<<elem->x<<","<<elem->y<<")"<<"->"<<elem->outgoing;
}
//此处借用dascpp中邓公的随机迷宫生成程序
static int labySize;  
static Cell* startCell;
static Cell* goalCell;
//随机迷宫
static void randLaby()
{
	labySize = LABY_MAX / 2 + rand() % (LABY_MAX / 2); //生成一个随机size的迷宫
	cout << "一个大小为"<<labySize<<"的迷宫,";
	//printf("Using a laby of size %d ...\n", labySize);
	for (int i = 0; i < labySize; i++)
		for (int j = 0; j < labySize; j++)
		{
			laby[i][j].x = i;
			laby[i][j].y = j;
			laby[i][j].incoming =laby[i][j].outgoing = UNKNOWN;
			laby[i][j].status = WALL; //边界格点必须是墙
		}
	for (int i = 1; i < labySize - 1; i++)
		for (int j = 1; j < labySize - 1; j++)
			if (rand() % 4) laby[i][j].status = AVAILABLE; //75%的格点为空可用
	startCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
	goalCell = &laby[rand() % (labySize - 2) + 1][rand() % (labySize - 2) + 1];
	startCell->status = goalCell->status = AVAILABLE; //起始格点必须可用
}

//迷宫显示
static void displayLaby() { //┘└┐┌│─
	

	// char  pattern[5][5] =
	//{
	//	'┼', '┼', '┼', '┼', '┼',
	//	'┼', '  ', '┌', '─', '└',
	//	'┼', '┌', '  ', '┐', '│',
	//	'┼', '─', '┐', '  ', '┘',
	//	'┼', '└', '│', '┘', '  '
	//};
	 const char*  pattern[5][5] =
	{
		   "┼", "┼", "┼", "┼", "┼",
			"┼", "  ", "┌", "─", "└",
			"┼", "┌", "  ", "┐", "│",
			"┼", "─", "┐", "  ", "┘",
			"┼", "└", "│", "┘", "  "
	};

	//system("cls");//清屏操作
	 cout << " ";
	 for (int j = 0; j < labySize; j++)//输出迷宫的列号
		if (j < 10)
		 cout << hex << j;
		else cout << (char)('A' + j - 10);
		//(j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
	cout << endl;
	for (int j = 0; j < labySize; j++)//输出迷宫的行号
	{
		if (j < 10)
			cout << hex << j;
		else cout<< (char)('A'+j-10);
		//(j < 10) ? printf("%2X", j) : printf(" %c", 'A' - 10 + j);
		for (int i = 0; i < labySize; i++)
			if (startCell == &laby[i][j])
				cout << "○";//出生点打印出○
			else if (goalCell == &laby[i][j])
				cout << " $";//终点打印$
			else
				switch (laby[i][j].status)
				{
					
				case WALL:  cout << "█";//如果是墙打印█
					//printf("█"); 
					break;
				case BACKTRACKED: cout << "*";//回溯过的打印*
					//printf("*");  
					break;
				case AVAILABLE: cout << " ";//可以同行的打印空
					// printf("  ");  
					break;
				default:
					cout << pattern [laby[i][j].outgoing][laby[i][j].incoming]<<" ";
					break;
				}
		cout << endl;
	}
}

int main() {


	//栈的应用-----进制转换器
	//stack<char> q;
	//static char digit[] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
	//cout << "请输入要转换的十进制数:";
	//int x; cin >> x;
	//cout << "请输入要转换的进制(最高支持十六进制):";
	//int y; cin >> y;
	//if(y>16){
	//	cout << "您输入的进制太大了!"; 
	//	return 0;
	//}
	//while (x > 0)
	//{
	//	q.push(digit[x%y]);
	//	x /=  y;
	//}
	//while (q.size())
	//{
	//	cout << q.top() << " ";
	//	q.pop();
	//}
	//cout << endl;
	//system("pause");
	//return 0;
	
	/*
	//括号匹配
	cout << "请任意输入一个括号序列";
	string schar;
	cin >> schar;
	cout<<(bool)paren(schar);
	//计算括号表达式
	 char s[11] = {'0','+','1','*','(','5','!','-','2',')','\0'};
	 char o[6] = {'2','4','+','2','0','\0'};//24+20 ===44
	 cout << endl;
	 cout<< evaluate(s);
	 cout << endl;
	 cout<<evaluate(o);
	 cout << endl;
	
	 cout << "转换成逆波兰表达式:";
	 char rpn[11] = { 0 };
	 RPM(s, rpn);
	 for (int i = 0; i < 11; i++)
		cout << rpn[i] << " ";
		*/


	//N皇后
	//placeQueens(8);//输入4,答案是2
	//cout << endl;
	 
	//迷宫寻径
	
	srand(int(time(0)));  //根据系统时间确定随机种子,保证每次执行都不同
	randLaby();//随机制造一个迷宫
	if (labyrinth(laby, startCell, goalCell))
		cout << "创造成功!" << endl;
	else
		cout << "创造失败!" << endl;
	displayLaby();//迷宫显示
	cout << "开始位置: "<<"○" << "(" << dec <<startCell->x << "," << dec<<startCell->y << ")"
		<< "  " << "终点位置: "<<"$"  << "(" << dec<<goalCell->x << "," << dec<<goalCell->y << ")" << endl;
	cout << "检查时间: " << dec <<ncheck << " 返回时间: " << dec <<nback << endl;
	cout << "路径的长度是 " << length << endl;
	
	system("pause");
	return 0;
	
}

 以上所有代码参考邓俊辉老师的《c++语言版数据结构》

猜你喜欢

转载自blog.csdn.net/qq_58240448/article/details/127026679