(四)栈和队列的应用

  • 数制转换
//目标:对于输入的任意一个非负十进制整数,打印输出与其等值的八进制数
//思路:1-初始化栈 2-对8求余,余数推入栈中 3-除等8 4-从栈中取出
void conversion() {
 InitStack(S);
 scanf("%d",N);
 while(N) {
   Push(S, N % 8);//对8求余 余数推入栈
   N = N/8;//每次整除8
 }
 while(!StackEmpty(s)) {
   Pop(S, e);
   printf("%d", e);
 }
}
//栈的引入简化了程序设计的问题,划分了不同的关注层次,使思考范围缩小了,而用数组不仅掩盖了问题的本质,还要分散精力去考虑数组下标增减等细节问题
  • 括号匹配的校验
//目标:括号匹配的校验
//思路:使用栈来解决、遇到`([`就`入栈`,遇到`)]`就出栈,遇到其他元素不做处理。处理完后,检查栈的长度,为空表明括号匹配。
int checkBracket() {
  SqStack s, *sp = &s;
  [InitStack](https://www.cnblogs.com/cgy-home/p/15236421.html)(sp);
  char str[20];
  scanf("%s",str);
  if (str[0] == ']' || str[0] == ')')
    exit(1);

  char c,d,*cp=&c,*dp=&d;
  for (int i=0;c=str[0];c!='\0';i++) {
    c = str[i];
    if(!StackEmpty) 
      GetTop(s,dp);

    char t,*tp = &t;
    switch(c) {
      case '(':Push(sp,c);break;
      case '[':Push(sp,c);break;
      case ')':d=='('?Pop(sp,tp):Push(sp,c);break;
      case ']':d=='['?Pop(sp,tp):Push(sp,c);break;
      default:break;
    }
  }
  return StackEmpty(sp) ? 1 : 0;
}
  • 行编辑程序
//目标:接收用户从终端输入存入缓冲区(缓冲区目的是为了允许用户输入出差错可以及时改正),#退格符,表示前一个字符无效,@退行符,当前行中的字符无效
//思路:构建栈,#弹出栈顶元素,@清空栈,默认压栈
void LineEdit() {
  //利用字符栈S,从终端接收一行并传送至调用过程的数据区。
  InitStack(S);//构造空栈S
  ch = getchar();//从终端接收第一个字符
  while(ch != EOF) {//EOF为全文结束符
    while(ch != EOF && ch != '\n') {
        switch(ch) {
          case '#': Pop(S, c); break;//仅当栈非空时退栈
          case '@': ClearStack(S); break;//重置S为空栈
          default: Push(S, ch); break;//有效字符进栈,未考虑找满情形
        }
        ch = getchar();//从终端接收下一个字符
    }
    //将从栈底到栈顶的栈内字符传送至调用过程的数据区;
    ClearStack(S);//重置S为空栈
    if (ch != EOF) ch = getchar();
  }
  DestroyStack(S);
}//LineEdit

  • 迷宫求解
//目标:求迷宫中从入口到出口的所有路径;从入口出发,顺某一方向探索,若能走通,则继续向前走;否则沿原路退回,换个方向再继续弹错,直至所有可能的通路都探索为止。
//思路:求迷宫中一条路径的算法,若当前位置可通,则推入栈中,继续下一位置并作为当前位置,如此重复;若当前位置不通,则退回上一位置,然后朝其他方向探索;若该通道块四周都不通,则从栈顶删除该位置。
//求迷宫中一条从入口到出口的路径的算法简单描述:
//设定当前位置的初值为入口位置:
//do{
//  若当前位置可通,
//  则{ 将当前位置插入栈顶;//纳入路径
//      若该位置是出口位置,则结束;//求得路径存放在栈中
//      否则切换当前位置得东邻方块为新得当前位置;
//}
//  否则,
//    若栈不空且栈顶位置尚有其他方向未经探索,
//      则设定新的当前位置为沿顺时针方向旋转找到得栈顶位置得下一相邻块;
//    若栈不空但栈顶位置得四周均不可通,
//      则{ 删去栈顶位置;//从路径中删去该通道块
//        若栈不空,则重新测试新的栈顶位置,直至找到一个可通的相邻块或出栈至栈空;
//      }
//}while(栈不空);
//注,当前位置`可通`,指的是`未曾走到过的通道块`,即要求该通道块位置不仅是通道块,
//  而且即不在当前路径上(否则所求路径就不是简单路径),也不是曾经那如果路径的通道块(否则只能在死胡同内转圈)
typedef struct {
	int ord;//通道块在路径上的“序号”
	PosType seat;//通道块在迷宫中的“坐标位置”
	int di;//从此通道块走向下一通道块的“方向”
}SElemType;//栈的元素类型

Status MazePath(MazePath maze, PosType start, PosType end) {
  //若迷宫maze中存在入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE
  InitStack(S); curpos = start;//设定“当前位置”为“入口位置”
  curstep = 1;//探索第一步
    do{
      if(Pass(curpos)) {//当前位置可以通过,即是未曾走到过的通道块
          FootPrint(curpos);//留下足迹
          e = (curstep, curpos, 1);
          Push(S, e);//加入路径
          if (curpos == end) return (TRUE);//到达出口
          curpos = NextPos(curpos, 1);//下一位置是当前位置的东邻
          curstep++;//探索下一步
      }//if
      else{//当前位置不能通过
        if (!StackEmpty(S)) {
          Pop(S, e);
          while(e.di == 4 && !StackEmpty(S)) {
            MarkPrint(e.seat); Pop(S,e);//留下不能通过的标记,并退回一步
          }//while
          if(e.di < 4) {
            e.di++; Push(S,e);//换下一个方向探索
            curpos = NextPos(e.seat,e.di);//设定当前位置是该新方向上的相邻块
          }//if
        }//if 
      }//else 
  }while(!StackEmpty(S));
  return (FALSE);
}//MazePath

  • 表达式求值
//目标:算符优先法,求表达式值,算数四则运算规则,先乘除,后加减,从左到右,先括号内后括号外;
//思路:使用两个栈,一个存操作数(operand),一个存运算符(operator)
OperandType EvaluateExpression() {
  //算数表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈,OP为运算符集合
  InitStack(OPTR); Push(OPTR,'#');
  initStack(OPND); c = getchar();
  while(c != '#' || GetTop(OPTR) != '#') {
    if (!In(c, OP)) {Push(OPND,c); c = getchar();}//不是运算符则进栈
    else 
      switch(Precede(GetTop(OPTR,c))) {
        case '<'://栈顶元素优先权低
          Push(OPTR,c); c= getchar();
          break;
        case '='://脱括号并接收下一字符
          Pop(OPTR,x); c = getchar();
          break;
        case '>'://退栈并将运算结果入栈
          Pop(OPTR,theta);
          Pop(OPND,b); Pop(OPND,a);
          Push(OPND, Operate(a, theta, b));//Operate二元运算,如果是解释执行表达式,则直接进行该结果并返回
          break;
      }//switch
    }//while
    return GetTop(OPND);
}//EvaluateExpression

char Precede(char a, char b) {//获取操作符优先级 < > = 
  if (a == '+' || a == '-') {
    if (b == '+'||b == '-'||b == ')'||b == '#') return '>';
    else return '<';
  } else if (a == '*'||a == '/') {
    if (b == '+'||b == '-'||b == ')'||b == '#'||b == '*'||b == '/') return '>';
    else return '<';
  } else if (a == '(') {
    if (b ==  ')') return '=';
    else return '<';
  } else if (a == ')') {return '>';
  } else {
    if (b == '#') return '=';
    else return '<';
  }  
}

int Operator(int a, char s, int b) {}//获取两个数的运算结果

队列

  • 离散事件模拟
//目标:编制一个程序模拟银行的这种业务活动并计算一天中客户在银行逗留的平均时间。
//思路:每个银行窗口在某个时刻只能接待一个客户,对于刚进入银行的客户,如果某个窗口的业务员正在空闲,则可上前办理业务,反之,若4个窗口均有客户所占,便排在人数最少的队伍后面。需要制定每个客户到达银行和离开银行的两个时刻,相减得逗留时间。所有客户逗留时间总和除以客户数即平均时间。
void Bank_Simulation(int CloseTime) {
  //银行业务模拟,统计一天内客户在银行逗留得平均时间。
  OpenForDay();//初始化
  while (MoreEvent) {
    EventDrived(OccurTime, EventType);//事件驱动
	switch(EventType) {
	  case 'A':CustomerArrived();break;//处理客户到达事件,客户到来
	  case 'B':CustomerDeparture();break;//处理客户离开事件,处理事务所需事件和等待时间所定
	  default:Invalid();
	}
  }
  CloseForDay;//计算平均逗留事件
}
//需要得数据结构及操作,有序链表和队列
//在任何时刻即将发生的事件只有5种可能:新的客户到达、1号窗口客户离开、2号窗口客户离开、3号窗口客户离开、4号窗口客户离开。
typedef struct{
  int OccurTime;//事件发生时刻
  int NType;//事件类型,0表示到达事件,1-4表示四个窗口得离开事件
}Event,ElemType;//事件类型,有序链表LinkList得数据元素类型

typedef LinkList EventList//事件链表定义,定义为有序链表

typedef struct {
  int ArrivalTime;//到达时刻
  int Duration;//办理事务所需时间
}QElemType;//队列得数据元素类型
//模拟两个随机数,durtime-所需时间,intertime-写个客户到达时间间隔,occurtime+intertime-下个客户到达事件发生得时刻。
EventList ev;//事件表
Event en;//事件
LinkQueue q[5];//4个客户队列
QElemType customer;//客户记录
int TotalTime, CustomerNum;//累计客户逗留事件,客户数

int cmp(Event a, Event b);//依事件a得发生时刻< = >事件b得发生时刻返回-1 0 1

void OpenForDay() {
  //初始化操作
  TotalTime = 0; CustomerNum = 0;//初始化累计时间和客户数为0
  InitList(ev);//初始化事件链表为空表
  en.OccurTime = 0; en.NType = 0;//设定第一个客户到达事件
  OrderInsert(ev,en,cmp);//插入事件表
  for (i = 1;i<=4;++i) InitQueue(q[i]);//置空队列
}

void CustomerArrived() {
  //处理客户到达事件,en.NType = 0
  ++CustomerNum;
  Random(durtime, intertime);//生成随机数
  t = en.OccurTime + intertime;//下一客户到达时刻
  if (t < CloseTime) //银行尚未关门,插入事件表
    OrderInsert(ev,(t,0),cmp); 
  i = Minimum(q);//求长度最短队列
  EnQueue(q[i],(en.OccurTime,durtime));
    OrderInsert(ev,(en.OccurTime+durtime,i),cmp);
	//设定第i队列得一个离开事件并插入事件表
}

void CustomerDeparture() {
  //处理客户离开事件,en.NType>0;
  i = en.NType; DelQueue(q[i],customer);//删除第i队列的排头客户
  TotalTime += enOccurTime - customer.ArrivalTime;//累计客户逗留时间
  if (!QueueEmpty(q[i])) {//设定第i队列的一个离开事件并插入事件表
    GetHead(q[i],customer);
	Orderinsert(ev, (en.OccurTime+customer.Duration,i),(*cmp)());
  }
}

void Bank_Simulation(int CloseTime) {
  OpenForDay();//初始化
  while(!ListEmpty(ev)) {
    DelFirst(GetHead(ev),p); en = GetCurElem(p);
	if(en.NType==0)
	  CustomerArrived(); //处理客户到达事件
	else CustomerDeparture(); //处理客户离开事件
  }
  //计算并输出平均逗留时间
  printf("The Average Time is %f\n", (float)TotalTime/CustomerNum);
}

目录

posted @ 2021-09-13 15:04  白玉神驹  阅读(380)  评论(0编辑  收藏  举报