您的位置:首页 > 其它

栈的应用

2015-07-14 20:28 330 查看
学习数据结构一年了,现在才有点时间开始慢慢总结之前学过的一些东西,数据结构的好坏体现的 是一个程序员的基本功。数据结构和算法是一体的,当我们研究数据结构时,通常也涉及到算法。平时没有养成一个好的习惯,将很多东西都忘记了,以后努力将每天所学到的知识进行一个总结。闲话不多说。

栈,是一种在表尾进行插入或者删除的线性表。其特点为后进先出(LIFO),存储结构分为顺序存储或者链式存储。具体的概念性的东西可以参照一下严蔚敏版数据结构。下面就谈一下栈的三个有意思的应用:括号匹配,迷宫求解,表达式求值

括号匹配算法:

括号匹配算法的思想非常简单,遍历输入的字符串,如果开始输入的是右括号)或者],则不能够匹配。其他情况下首先将左括号压入栈(或者[,然后在访问下一个字符,若还是左括号,继续入栈。当遇到有括号的时候,判断一下是否与前面一个符号相匹配,若匹配,则进行出栈操作。然后访问下一个符号,重复上面的操作。

#include<iostream>
using namespace std;
#define ERROR 0
#define OK 1
#define STACK_INIT_SIZZE 100
#define STACKINCREMENT 10
#define SElemType char

typedef struct SqStack{

SElemType *base;//栈底指针
SElemType *top;//栈顶指针
int size;
}SqStack;

//初始化一个栈
int InitStack( SqStack &s)
{
s.base=(SElemType*)malloc(STACK_INIT_SIZZE*sizeof(SElemType));
if(s.base==NULL)return ERROR;

s.top=s.base;//设置栈表为空
s.size=STACK_INIT_SIZZE;//当前栈的长度
return OK;
}

int StackEmpty(SqStack s)
{
if(s.base==s.top)//当栈顶等于栈底的时候说明栈为空
return OK;
return ERROR;
}

//入栈
int Push(SqStack &s, SElemType e)
{
if(s.top-s.base>STACK_INIT_SIZZE)
{
s.base=(SElemType*)realloc(s.base,(s.size+STACKINCREMENT)*sizeof(SElemType));//如果内存满了重新分配内存空间
if(!s.base)exit(ERROR);//内存分配失败
s.top=s.base+STACK_INIT_SIZZE;
s.size+=STACKINCREMENT;//将容量扩大

}
*(s.top++)=e;

return OK;
}

//如果栈表不为空,则删除s的栈顶元素,并用e将其值返回回来
int Pop(SqStack &s,SElemType &e)
{
if(s.base==s.top)return ERROR;
e=*(--s.top);
return OK;

}

void DestoryStack(SqStack &s)
{
free(s.base);
}

int StackLength(SqStack s)
{
int i=0;
while(s.base!=s.top)
{
i++;
s.top--;
}

return i;
}

//判断输入的数据是否匹配
int Match(char s[],SqStack &l)
{
printf("%s\n",s);
int i,j=0;
int length=strlen(s);
char *a=new char[];
for(i=0;i<length;i++)
{
if(StackEmpty(l))
{
if(s[i]==')'||s[i]==']')
return ERROR;
else if(s[i]=='('||s[i]=='[')
Push(l,s[i]);
}
else if(!StackEmpty(l))
{
if(s[i]==')')
if(*(l.top-1)=='(')
Pop(l,a[j++]);//将该括号弹出栈
if(s[i]==']')
if(*(l.top-1)=='[')
Pop(l,a[j++]);
if(s[i]=='('||s[i]=='[')
Push(l,s[i]);
}
}
if(StackEmpty(l))
return OK;
return ERROR;

}

int main()
{
SqStack l;
char s[100];
scanf("%s",&s);
InitStack(l);
if(Match(s,l))
printf("括号是匹配的!\n");
else
printf("括号不匹配!\n");
if(l.top==l.base)
printf("栈已经被清空!\n");
return 0;
}
迷宫求解:

迷宫求解问题是一个经典的程序设计问题,计算机在解决此类问题的时候通常使用的穷举法,具体思想是:从入口出发,沿着某一特定的方向向前进行探索,若走得通,就继续向前走;否则原路退回,换一个方向再继续探索。直到将所有可能的路径都探索到为止。为了保证任何路径都能沿原路返回,就需要一个后进先出的结构来保存路径,所以就要用到栈。

其描述如下:

设置当前位置的初值为栈的入口位置;

do{

若当前位置可通 //将当前位置纳入路径

则{

将当前位置进行入栈处理,并将当前位置标记下来

若当前位置为出口位置,则结束

否则将当前位置东边的邻块设置为新的当前位置

}

否则

{

若栈不为空,且栈顶元素还有其他位置方向没有探索

则设定新的当前位置为沿顺时针方向找到栈顶位置的下一个相邻块

栈不为空,当时栈顶位置的四周都不通

{

删除栈顶元素

若栈还不为空则重新测试新的栈顶位置

直到找到一个可通的相邻块或者或者栈为空

}

}

}while(栈不为空)

可通是指当前未曾走到的通道块。不然会形成循环。

#include<iostream>
using namespace std;
#define Status int
#define OK 1
#define ERROR 0
#define STACK_SIZE 100
#define INCREMENT_SIZE 10

int MAZE[10][10]=
{
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,1,1,0,0,1,1,0},
{0,1,0,0,0,1,1,1,1,0},
{0,1,1,1,0,1,1,1,1,0},
{0,1,0,1,1,1,0,1,1,0},
{0,1,0,0,1,1,1,0,1,0},
{0,0,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0}
};

typedef struct{
int x;
int y;
}PosType;

typedef struct{

int ord;//通道块路径上面的序号
PosType seat;//通道块在迷宫当中的坐标
int di;//从此通道块向下一通道块所走的方向;
}SElemType;

typedef struct{

SElemType *top;//栈顶元素
SElemType *base;//栈底元素
int size;
}SqStack;

Status InitStack(SqStack &S);
void Push(SqStack &S,SElemType e);
void Pop(SqStack &S,SElemType &e);
Status StackEmpty(SqStack S);

//地图探索的操作
int Pass(PosType pos);
void FootPrint(PosType pos);
void MarkPrint(PosType pos);
PosType NextPos(PosType curpos,int di);
Status MazePath(PosType start,PosType end);
int Equals(PosType p1,PosType p2);

int main()
{
int i,j;
PosType start,end;
start.x=start.y=1;
end.x=end.y=8;

for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
printf("%d ",MAZE[i][j]);
printf("\n");
}

if(MazePath(start,end))
printf("该迷宫存在解\n");
else
printf("该迷宫不存在解\n");

for(i=0;i<10;i++)
{
for(j=0;j<10;j++)
printf("%d ",MAZE[i][j]);
printf("\n");
}
return 0;
}

Status InitStack(SqStack &S)
{
S.base=(SElemType*)malloc(STACK_SIZE*sizeof(SElemType));
if(!S.base)return ERROR;
S.top=S.base;
S.size=STACK_SIZE;
return OK;
}

void Push(SqStack &S,SElemType e)
{
if(S.top-S.base>=STACK_SIZE)
{
S.base=(SElemType*)realloc(S.base,(S.size+INCREMENT_SIZE)*sizeof(SElemType));
S.top=S.base+S.size;
S.size+=INCREMENT_SIZE;
}
*S.top++=e;
}

void Pop(SqStack &S,SElemType &e)
{
if(S.base==S.top)exit(0);
e=*(--S.top);
}

Status StackEmpty(SqStack S)
{
if(S.base==S.top)
return OK;
else
return ERROR;
}

int Pass(PosType pos)
{
if(MAZE[pos.y][pos.x]==1)
return OK;
return ERROR;
}

void FootPrint(PosType pos)//留下足迹
{
MAZE[pos.y][pos.x]=2;
}

void MarkPrint(PosType pos)
{
//	printf("(%d,%d)\n走不通\n",pos.y,pos.x);
MAZE[pos.y][pos.x]=0;
}

PosType NextPos(PosType Curpos,int di)
{
switch(di)
{
case 1:Curpos.x++;	break;//向东探索
case 2:Curpos.y++;break;
case 3:Curpos.y--;break;
case 4:Curpos.x--;break;
}
return Curpos;
}

int Equals(PosType p1,PosType p2)
{
if(p1.x==p2.x&&p1.y==p2.y)
return OK;
else
return ERROR;
}

Status MazePath(PosType start,PosType end)
{
SqStack S;
PosType curpos;
SElemType e;
int curstep=1;
curpos=start;
InitStack(S);
do{
if(Pass(curpos))//当前位置可通
{
FootPrint(curpos);
e.ord=curstep;
e.seat=curpos;
e.di=1;
Push(S,e);
if(Equals(curpos,end))return OK;
curpos=NextPos(curpos,1);
curstep++;
}else//如果当前位置不可通
{
if(!StackEmpty(S))
{
Pop(S,e);
while(e.di==4&&!StackEmpty(S))//留下不能通过的标记,并且退回一格
{
MarkPrint(e.seat);
Pop(S,e);
}
if(e.di<4)
{
e.di++;
Push(S,e);//换下一个方向进行探索
curpos=NextPos(e.seat,e.di);
}
}
}
}while(!StackEmpty(S));
return ERROR;;
}


表达式求值算:

表达式求值是程序设计语言编译中一个基本的问题,他是实现栈的又一个经典的例子。通常使用的方法称作"算符优先法"

要对表达式进行求值,首先要正确理解表达式,即要了解他的运算规则,下面以简单的加减乘除四则运算为例:

主要有下面几条规则:

(1)先乘除,后加减

(2)从左算到右

(3)先括号内后括号外

算符的对应的优先关系有以下三中:

a>b a的优先权高于b

a=b a的优先权等于b

a<b a的优先权小于b

根据上面的三种有限规则,构造下面的算符有限表



由规则3可知 a为+.-.*./时的优先性低于"("但高于")",由规则2可知,当a,b为相同的算符时,a>b。"#"是表达式结束符。为了算符简洁,在表达式的最左边也虚设一个"#",使后面的算符有比较的对象,并且能够正常进入栈中。表中的"("=")"表示当左右括号相遇的时候,括号内的运算完成,然后进行去括号操作,一些算符之间没有优先关系是因为他们两两之间并不能够相邻。一旦出现相邻的情况,则认为语法错误。

为了实现算符优先运算,可以使用两个工作栈。一个称作oprt,用以寄存运算符,另一个称作opnd,用以存储操作数或者运算结果。

算法思想:

(1)首先将操作数栈设置为空栈,表达式起始符"#"为运算符栈的栈底元素

(2)一次读入表达式中的每一个字符,若是操作数,则进入操作数栈,若是运算f符,则和栈顶算符比较优先权既然后在进行相应的操作。假设a为栈顶运算符,b为读入的运算符,若若a>b,则在操作数栈中弹出两个操作数,然后将栈顶运算符弹出,进行对应的运算,再将结果压入操作数栈。若a<b,则将b压入运算符栈。若a=b则进行去括号处理。直到读入的字符为"#"或者栈顶元素为"#"

源代码:

#include<iostream>
using namespace std;

#define OK 1
#define ERROR -1
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define Status int
#define ElemType char
#define OPSETSIZE 8

typedef struct Operand{//操作数

double *base;//栈底指针
double *top;//栈顶指针
int size;
}Operand;

typedef struct Operator{

char *base;//栈底指针
char *top;//栈顶指针
int size;
}Operator;

Status InitOprd(Operand &oprd)
{
oprd.base=(double*)malloc(STACK_INIT_SIZE*sizeof(double));
if(oprd.base==NULL)return ERROR;
oprd.top=oprd.base;
oprd.size=STACK_INIT_SIZE;
return OK;
}

Status InitOprt(Operator &oprt)
{
oprt.base=(char*)malloc(STACK_INIT_SIZE*sizeof(char));
if(oprt.base==NULL)return ERROR;
oprt.top=oprt.base;
oprt.size=STACK_INIT_SIZE;
return OK;
}

Status PushOprd(Operand &oprd,double e)
{
if(oprd.top-oprd.base>STACK_INIT_SIZE)
{
oprd.base=(double*)realloc(oprd.base,(oprd.size+STACKINCREMENT)*sizeof(double));
if(oprd.base==NULL)exit(0);
oprd.top=oprd.base+STACK_INIT_SIZE;
oprd.size+=STACKINCREMENT;
}
*oprd.top=e;
oprd.top++;
return OK;
}

Status PushOprt(Operator &oprt,ElemType e)
{
if(oprt.top-oprt.base>STACK_INIT_SIZE)
{
oprt.base=(char*)realloc(oprt.base,(oprt.size+STACKINCREMENT)*sizeof(char));
oprt.top=oprt.base+STACK_INIT_SIZE;
oprt.size+=STACKINCREMENT;
}
*(oprt.top)=e;
oprt.top++;
return OK;
}

Status PopOprd(Operand &oprd,double &e)
{
if(oprd.base==oprd.top)return ERROR;
e=*(--oprd.top);
return OK;
}

Status PopOprt(Operator &oprt,ElemType &e)
{
if(oprt.base==oprt.top)return ERROR;
e=*(--oprt.top);
return OK;
}

char getOprdTop(Operand oprd)
{
ElemType e;
if(oprd.base==oprd.top)return ERROR;
e=*(--oprd.top);
return e;

}

char getOprtTop(Operator oprt)
{
ElemType e;
if(oprt.base==oprt.top)return ERROR;
e=*(--oprt.top);
return e;
}

char getOprtBase(Operator oprt)
{
ElemType e;
if(oprt.base==oprt.top) return ERROR;
e=*(oprt.base);
return e;
}

int getOprtSize(Operator oprt)
{
int i=0;
if(oprt.base==oprt.top)return 0;

while(oprt.base!=oprt.top)
{
oprt.base++;
i++;
}

return i;
}

int getOprdSize(Operand oprd)
{
int i=0;
if(oprd.base==oprd.top)return 0;

while(oprd.base!=oprd.top)
{
oprd.base++;
i++;
}

return i;
}

unsigned char Prior[8][8] = {   //运算符优先级表
'>','>','<','<','<','>','>','>',
'>','>','<','<','<','>','>','>',
'>','>','>','>','<','>','>','>',
'>','>','>','>','<','>','>','>',
'<','<','<','<','<','=',' ','>',
'>','>','>','>',' ','>','>','>',
'<','<','<','<','<',' ','=','>',
'<','<','<','<','<','<','<','='
};

char OPSET[OPSETSIZE]={'+' , '-' , '*' , '/' ,'(' , ')' , '#','\n'};

int ReturnOpOrd(char op,char* TestOp)
{
for(int i=0;i<OPSETSIZE;i++)
if(op==TestOp[i])
return i;
return 0;
}

int In(char op,char* TestOp)
{
int find=0;
for(int i=0;i<OPSETSIZE;i++)
if(op==TestOp[i])
find=1;

return find;
}

char Precede(char op1,char op2)//比较运算符的优先级
{
return Prior[ReturnOpOrd(op1,OPSET)][ReturnOpOrd(op2,OPSET)];
}

double Operate(double a,char op,double b)
{
switch(op){
case '+':return a+b;break;
case '-':return a-b;break;
case '*':return a*b;break;
case '/':return a/b;break;

}
}

double EvaluateExpression(Operand &oprd,Operator &oprt)
{
char c;
c=getchar();

while(c!='\n'||getOprtSize(oprt)!=0)//执行到最后操作符栈只剩下换行符
{
if(!In(c,OPSET))
{
c=c-'0';
PushOprd(oprd,c);
c=getchar();
}
else//如果是操作符
{
if(getOprtSize(oprt)==0)//判断栈是否为空
{
PushOprt(oprt,c);
c=getchar();
}
else
{
switch(Precede(getOprtTop(oprt),c)){
case '>':
double a,b;
if(PopOprd(oprd,a))
if(PopOprd(oprd,b))
{
char operate;
PopOprt(oprt,operate);
PushOprd(oprd,Operate(b,operate,a));
}
break;
case '=':
char ss;
PopOprt(oprt,ss);
c=getchar();
break;
case '<':
PushOprt(oprt,c);
c=getchar();
break;
}
}
}
}
double result;
PopOprd(oprd,result);
printf("%lf\n",result);
return result;
}

main()
{
Operand oprd;
Operator oprt;
InitOprt(oprt);
InitOprd(oprd);
EvaluateExpression(oprd,oprt);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: