栈的应用

来源:互联网 发布:js把date转换成字符串 编辑:程序博客网 时间:2024/06/07 07:50

 学习数据结构一年了,现在才有点时间开始慢慢总结之前学过的一些东西,数据结构的好坏体现的 是一个程序员的基本功。数据结构和算法是一体的,当我们研究数据结构时,通常也涉及到算法。平时没有养成一个好的习惯,将很多东西都忘记了,以后努力将每天所学到的知识进行一个总结。闲话不多说。
   栈,是一种在表尾进行插入或者删除的线性表。其特点为后进先出(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");elseprintf("括号不匹配!\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 10int 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");elseprintf("该迷宫不存在解\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;elsereturn 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;elsereturn 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 8typedef 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;}

   

0 0