(四)棧和隊列的應用


  • 數制轉換
//目標:對於輸入的任意一個非負十進制整數,打印輸出與其等值的八進制數
//思路: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);
}

目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM