数据结构——栈和队列的应用(进制转化,表达式求值,迷宫问题)

栈的应用

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

  1. 前缀式: + * a b * - c / d e f
  2. 中缀式: a * b + c - d / e * f
  3. 后缀式: 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;

我们还需要标记走过的路,这里有两种方法

  1. 在用一个一样的数组,开始都是0,走过的变为1
  2. 直接在原来数组中,走过的标记为-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("没有路径");
}

在这里插入图片描述

我们仔细体会一下广搜的妙处,路径长的,他搜索 起来经过的步骤就多一些,那么在队列中就没有路径短的走的直接(比较抽象,可以用笔画一画),这里多体会一下下


栈和队列还有很多其他的应用,比如队列有银行排队问题,等 有兴趣可以了解一下

不重要的事分隔符
我又来要赞来的啊,要赞当然很重要了,如果觉得可以学到点什么,点个赞再走吧,欢迎各位路过的大佬评论指正

发布了50 篇原创文章 · 获赞 50 · 访问量 2708

猜你喜欢

转载自blog.csdn.net/weixin_45691686/article/details/105038780