栈的应用
1.进制转换
进制转化主要的数学依据是短除法
//栈的应用 1
#include<stdio.h>
#include<stdlib.h>
const int maxn=1e6,mod=8;//这里转化八进制
typedef struct {
int a[maxn];
int top;
}SqStack;
void Init(SqStack *&s){
s= (SqStack *)malloc(sizeof(SqStack));
s->top=-1;
}
bool push(SqStack *&s,int e){
if(s->top==maxn-1)
return false;
else
{
s->top++;
s->a[s->top]=e;
return true;
}
}
bool pop(SqStack *&s,int &e){
if(s->top==-1)
return false;
else{
e= s->a[s->top];
s->top--;
return true ;
}
}
void conversion(SqStack *&s,int x){//这是转化的函数
int e;
while(x){
push(s,x%mod);
x/=mod;
}
}
int main(){
int x,e;
SqStack *s;
Init(s);
printf("输入一个十进制数:");
scanf("%d",&x);
conversion(s,x);
while(s->top!=-1){
pop(s,e);
printf("%d",e);
}
}
2.表达式求值
这里有好几种表达式
Exp = a * b + (c + d / e) *f
- 前缀式: + * a b * - c / d e f
- 中缀式: a * b + c - d / e * f
- 后缀式: a b * c d e / - f * +
这里我们主要演示对中缀表达式的求值,体会数据栈和运算符栈的巧妙
首先我们要思考,求值时,我们要考虑运算符的优先级顺序,先括号,再乘除,后加减,所以我们进栈出栈的时候都要考虑这个,其次我们注意操作数,每次是两个数运算,所以要考虑好顺序,下图是操作数的优先级,第一列是在栈中的,第一行是读入的。
- 当栈中的优先级小于栈外时,将操作符压入栈中
- 当栈中的优先级大于栈外时,进行计算
- 当栈中的优先级等于栈外时,可以消去一对括号
//栈的应用_2 表达式求值
#include<stdio.h>
#include<stdlib.h>
const int maxn=1e6;
typedef struct linknode{
char data[maxn];
int top;
}SqStack;
void Init(SqStack *&OP){
OP=(SqStack*)malloc(sizeof(SqStack));
OP->top=-1;
}
bool push(SqStack *&OP,char x){//入栈
if(OP->top==maxn-1)
return false;
else{
OP->top++;
OP->data[OP->top]=x;
return true;
}
}
bool pop(SqStack *&OP,char &x){//出栈
if(OP->top==-1)
return false;
else{
x=OP->data[OP->top];
OP->top--;
return true;
}
}
char getc(SqStack *&OP){
return OP->data[OP->top];
}
double doop(char A,char c,char B){//进行计算
A=A-'0';
B=B-'0';
char anss;
if(c=='-'){
anss = A-B;
anss=anss+'0';
return anss;
}
else if(c=='+'){
anss = A+B;
anss=anss+'0';
return anss;
}
else if(c=='/'){
anss =A/B;
anss=anss+'0';
return anss;
}
else{
anss = A*B;
anss=anss+'0';
return anss;
}
}
int cmp(char i,char o){//是栈里的,o是栈外的 ,比较两个操作符的优先级
if(i=='-'||i=='+'){
if(o=='+'||o=='-'||o=='#'||o==')')
return 1;
else
return -1;
}
else if(i=='*'||i=='/'){
if(o=='(')
return -1;
else return 1;//1 代表优先级 i>o
}
else if(i=='('){
if(o==')')
return 0;
else return -1;
}
else if(i==')')
return 1;
else {
if(o=='#')
return 0;
else return -1;
}
}
bool Isnum(char x){//判断读入的字符是数字还是操作符
if(x!='+'&&x!='-'&&x!='*'&&x!='/'&&x!='('&&x!=')'&&x!='#')
return true;
else return false;
}
//3*(4+1)*5#
int main(){
char st,cc,a,b;
SqStack *OPND;//数字栈
SqStack *OPIT;//运算符栈
Init(OPND),Init(OPIT);
push(OPIT,'#');
printf("请输入表达式以#结束:");
st=getchar();
do{
if(Isnum(st)){
push(OPND,st);
st=getchar();
}
else{
if(cmp(getc(OPIT),st)==0 ){
pop(OPIT,cc);
st=getchar();
}
else if(cmp(getc(OPIT),st)==1 ){
pop(OPIT,cc);
pop(OPND,a);
pop(OPND,b);
push(OPND,(char)doop(b,cc,a));
}
else if(cmp(getc(OPIT),st)==-1 ){
push(OPIT,st);
st=getchar();
}
}
}while(st!='#'||getc(OPIT)!='#');
pop(OPND,st);
st=st-'0';
printf("%d",st);
}
缺陷: 容易看出来这个是有缺陷的,因为读入的是字符,所以每次只能操作一位数,因为依靠ASCll 码来操作,所以 计算只能在 很小的位数中进行,超过ASCll码的位数就不对了。但没关系,我们主要是感受对栈的操作和优先级的巧妙设置
至于缺陷,我们可以了解一下后缀表达式的求值(等我有时间再写一下吧,
3. 迷宫问题
迷宫好玩啊,我都快写不出代码了,我们先来分析一下
我们迷宫有一个点,然后我们需要对上下左右四个方向进行试探,如果可以走就继续试探,如果有墙就返回上一个可以走的位置然后换方向试探,如此重复。(深度优先搜索)
先来看看我们结构体该如何定义
typedef struct{
int x
int y;//记录坐标
int di//记录当前方向(我们用0-3)表示上右下左即顺时针
}man;
我们还需要标记走过的路,这里有两种方法
- 在用一个一样的数组,开始都是0,走过的变为1
- 直接在原来数组中,走过的标记为-1;
很明显,第一个要额外用空间,这里我们用第二种方法
#include<stdio.h>
#include<stdlib.h>
using namespace std;
const int maxn = 100,N=8;
typedef struct{
int x;
int y;//记录坐标
int di;//记录试探的方向(我们用0-3)表示上右下左即顺时针
}man;
typedef struct{
man man_pre[maxn];
int top;
}SqStack;
int movex[]={0,1,0,-1};
int movey[]={1,0,-1,0};//移动的方向数组
//先把迷宫写出来
int map[N+2][N+2]={ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}};
void push(SqStack &s,man m){
s.top+=1;
s.man_pre[s.top]=m;
}
void pop(SqStack &s,man &mo){
mo = s.man_pre[s.top];
s.top--;
}
//找出口
bool dogo(SqStack &ss){
int line,col;
int x1,y1,di1;
man manmove={1,1,-1};
map[1][1]=-1;
ss.top=-1;
push(ss,manmove);
while (ss.top!=-1){
pop(ss,manmove);//是不是觉得先进栈再出栈有点多余,这其实是为了后面做准备。
x1=manmove.x;y1=manmove.y;di1=manmove.di+1;
while(di1<4){//四个方向都要试探
line = x1+movex[di1];
col=y1+movey[di1];
if(map[line][col]==0){//当这个时候试探的位置可以走的时候,入栈,并改变人的 位置
manmove.x=x1;
manmove.y=y1;
manmove.di=di1;
push(ss,manmove);//入栈当前位置
x1=line;y1=col;//改变人的位置
map[x1][y1]=-1;//站过的地方变为-1
if (x1==N&&y1==N)
return true;//看有没有到出口
else
di1=0;//新的点的开始还是向又试探
}
else di1++;
}//四个方向都完了,该回去退栈了
}
return false;
}
int main(){
SqStack ss;
man path;
if(dogo(ss)){
while(ss.top!=-1){
pop(ss,path);
printf("%d,%d,%d\n",path.x,path.y,path.di);
}
}
}
emmm,迷宫就这样结束了,但我们发现,找到的并不一定是最短的迷宫路径,下面我们来看看,如何找到最短的迷宫路径
队列
最短的迷宫路径
在使用栈求解的过程中,我们只能从一段进入,从同一端出栈,这就造成了我们需要在找到可以走的路后入栈,并在这个节点的基础上进行搜索。
但是,队列我们可以从队尾入栈,队头出栈,这就意味着,我们可以把当前节点能走的路全部压入队列,然后再从队头弹出元素,继续上面的操作。(广度优先搜索)
下面我们直接来看代码:
//用队列求解最短的路径。
#include<stdio.h>
#include<stdlib.h>
int movex[]={0,1,0,-1};
int movey[]={1,0,-1,0};
const int manx = 1e5,N=8;
typedef struct{
int i,j;
int pre;
}Box;
typedef struct{
Box data[manx];
int front, rear;
}QuType;//这里不用循环队列,因为我们要利用出队的 元素找到最短路径
int map[N+2][N+2]={ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}};
void print(QuType &qu,int x){
for(int i=x;i>=0;){
printf("(%d,%d)\n",qu.data[i].i,qu.data[i].j);
i=qu.data[i].pre;
}
//for(int i=x;i>=0;i--)
// printf(" 当前下标:%2d (%d,%d) 前一个下标:%2d\n",i,qu.data[i].i,qu.data[i].j,qu.data[i].pre);
}
bool mgpath1(int xi,int yi,int xe,int ye){
int i,j,find = 0,di;
QuType qu;
qu.front=qu.rear=-1;
qu.rear++;
qu.data[qu.rear].i=xi;
qu.data[qu.rear].j=yi;//(xi,yi)进队
qu.data[qu.rear].pre=-1;//第一个元素的前一个节点设置为-1;
map[xi][yi]=-1;//走过的变为-1;
while(qu.front!=qu.rear&&!find){
qu.front++;
i = qu.data[qu.front].i;
j = qu.data[qu.front].j;
if(i == xe&&j==ye){//找到了出口
find = 1;
print(qu,qu.front);
return true;
}
for(di = 0;di<4;di++){//把当前出队方块的每一个可以走的方块入队
i=qu.data[qu.front].i+movex[di];
j=qu.data[qu.front].j+movey[di];
if(map[i][j]==0){
qu.rear++;
qu.data[qu.rear].i=i;
qu.data[qu.rear].j=j;
//入队
qu.data[qu.rear].pre = qu.front;
//它们都是由同一个前置方块找到的可以走的路
//这个很关键,最后要靠这个来找最短的
map[i][j]=-1;
}
}
}
return false;
}
int main(){
if(!mgpath1(1,1,N,N))
printf("没有路径");
}
我们仔细体会一下广搜的妙处,路径长的,他搜索 起来经过的步骤就多一些,那么在队列中就没有路径短的走的直接(比较抽象,可以用笔画一画),这里多体会一下下
栈和队列还有很多其他的应用,比如队列有银行排队问题,等 有兴趣可以了解一下
—不重要的事分隔符
我又来要赞来的啊,要赞当然很重要了,如果觉得可以学到点什么,点个赞再走吧,欢迎各位路过的大佬评论指正