目录
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++语言版数据结构》