棧-佇列

小码king發表於2024-09-14

AcWing 828. 模擬棧

模板題:

實現一個棧,棧初始為空,支援四種操作:

  1. push x – 向棧頂插入一個數 x;
  2. pop – 從棧頂彈出一個數;
  3. empty – 判斷棧是否為空;
  4. query – 查詢棧頂元素。

現在要對棧進行 MM 個操作,其中的每個操作 3 和操作 4 都要輸出相應的結果。

輸入格式

第一行包含整數 M,表示操作次數。

接下來 M 行,每行包含一個操作命令,操作命令為 push xpopemptyquery 中的一種。

輸出格式

對於每個 emptyquery 操作都要輸出一個查詢結果,每個結果佔一行。

其中,empty 操作的查詢結果為 YESNOquery 操作的查詢結果為一個整數,表示棧頂元素的值。

資料範圍

1≤M≤1000001≤M≤100000,
1≤x≤1091≤x≤109
所有操作保證合法。

輸入樣例:

10
push 5
query
push 6
pop
query
pop
empty
push 4
query
empty

輸出樣例:

5
5
YES
4
NO

模擬棧的實現:

  • 棧的特性:先進後出,後出先進;
  • push x :棧頂所在索引往後移動一格,然後放入x。add[++t] = x;
  • pop : top 往前移動一格。-- t;
  • empty :top 大於等於 0 棧非空,小於 0 棧空。top == -1 ? “YES” : “NO”;
  • query : 返回棧頂元素。st[top];

AC程式碼

#include<iostream>
using namespace std;
const int D=100015;
int add[D],t;  //add[D]相當於一個棧,t就是一個"滑輪":實現增刪操作;
int main(){
    int n; //初始化
    cin >> n;
    string s;
    int m;
    while(n--){
        cin >> s;
        //這裡實現把數值壓入棧中:“++t"開闢一個空間,把值賦上
        if(s=="push"){
            cin >> m;
            add[++t]=m;
        //
        }else if(s=="pop"){
            --t;           //這裡同理:直接刪除這個空間;
        }else if(s=="query"){
            cout << add[t] << endl; //直接輸出(一系列的操作,滑輪停到那就輸出啥)
        }else if(s=="empty"){
            if(t>0){                
                cout << "NO" << endl;//有值就不為'No',無輸出'YES';
            }else{
                cout << "YES" << endl;
            }
        }
    }
}

AcWing 829. 模擬佇列

模板題:

實現一個佇列,佇列初始為空,支援四種操作:

  1. push x – 向隊尾插入一個數 x;
  2. pop – 從隊頭彈出一個數;
  3. empty – 判斷佇列是否為空;
  4. query – 查詢隊頭元素。

現在要對佇列進行 M 個操作,其中的每個操作 3 和操作 4 都要輸出相應的結果。

輸入格式

第一行包含整數 M,表示操作次數。

接下來 M 行,每行包含一個操作命令,操作命令為 push xpopemptyquery 中的一種。

輸出格式

對於每個 emptyquery 操作都要輸出一個查詢結果,每個結果佔一行。

其中,empty 操作的查詢結果為 YESNOquery 操作的查詢結果為一個整數,表示隊頭元素的值。

資料範圍

1≤M≤1000001≤M≤100000,
1≤x≤1091≤x≤109,
所有操作保證合法。

輸入樣例:

10
push 6
empty
query
pop
empty
push 3
push 4
pop
query
push 6

輸出樣例:

NO
6
YES
4

解題思路:

佇列:隊尾入隊,對頭出隊;先進先出,後進後出;

圖解

先定義陣列 e[n] 儲存資料;定義一個 hh 代表隊頭,e[hh]就代表對頭元素,e[hh+1]是第二個出隊的元素;

在定義一個 tt 代表隊尾,e[tt]就是隊尾元素,e[tt+1]是第二個隊尾插入的元素;

這個就跟單連結串列思想是一致的,剛開始讓要插入的元素指向“-1”,進行刪除的時候,指向這個節點的後一個節點就好了;

咱們舉個例子:佇列中的元素 2 3 4 5 我現在要刪除 “ 2 ” 咱們是不是隻需要記錄 “2” 對頭的後一個元素就好了,就在下一次的 query 操作輸出e[hh]就好了,hh 就只要負責 ++ 移到後一個數;

咱們要做的只是模擬,並不是向真正的資料結構一樣,實現刪除操作,咱們只需要做好模擬的事,不需要考慮它數值移到哪去了

AC程式碼

#include<iostream>
using namespace std;
const int N=100015;
//這裡的tt也可以定義0,那尾插就是tt++,hh<tt了(看個人習慣)
int e[N],hh,tt=-1;//hh對頭,tt隊尾
int main(){
    int n;
    cin >> n;
    string s;
    int m;
    while(n--){
        cin >> s;
        if(s=="push"){
            cin >> m;
            e[++tt]=m;
        }else if(s=="pop"){
            hh++;
        }else if(s=="query"){
            cout << e[hh] << endl;
        }else if(s=="empty"){
            if(hh<=tt){
                cout << "NO" << endl;
            }else cout << "YES" << endl;
        }
    }
}    

棧:表示式求值

實現棧思想例題:

給定一個表示式,其中運算子僅包含 +,-,*,/(加 減 乘 整除),可能包含括號,請你求出表示式的最終值。

注意:

  • 資料保證給定的表示式合法。
  • 題目保證符號 - 只作為減號出現,不會作為負號出現,例如,-1+2,(2+2)*(-(1+1)+2) 之類表示式均不會出現。
  • 題目保證表示式中所有數字均為正整數。
  • 題目保證表示式在中間計算過程以及結果中,均不超過 231−1231−1。
  • 題目中的整除是指向 00 取整,也就是說對於大於 00 的結果向下取整,例如 5/3=15/3=1,對於小於 00 的結果向上取整,例如 5/(1−4)=−15/(1−4)=−1。
  • C++和Java中的整除預設是向零取整;Python中的整除//預設向下取整,因此Python的eval()函式中的整除也是向下取整,在本題中不能直接使用。

輸入格式

共一行,為給定表示式。

輸出格式

共一行,為表示式的結果。

資料範圍

表示式的長度不超過 105105。

輸入樣例:

(2+2)*(1+1)

輸出樣例:

8

棧引導:

  • 現在給出一列算式 :
算式:1+1+1*5*6   //理解這一步:我們應該先算乘法嘛?這裡也可以看出,即使先算乘法,也不會影響 “1+1” 要執      =32                                                                           行這一步
  • 那知道這個特殊情況:上圖解 :

解析:(1)如果棧頂是+,即將入棧的是+,棧頂優先順序高,需要先計算,再入棧;

     (2)如果棧頂是+,即將入棧的是*,棧頂優先順序低,直接入棧;

     (3)如果棧頂是*,即將入棧的是+,棧頂優先順序高,需要先計算,再入棧;

     (4)如果棧頂是*,即將入棧的是*,棧頂優先順序高,需要先計算,再入棧;
  • i=j-1;問題
例子:2*10-10000+24-(5*3)+(3*2)   //在i=j-1之前的迴圈中,遍歷‘10’就讓,已經是甩開i了;
     就是說:i下標2指向數值‘1’的時候,j已經遍歷完了‘10’,如果直接讓j=i的話,就會跳過一些數值,
     實際情況自己模擬一下就知道了

AC程式碼

#include<iostream>
#include<algorithm>
#include <stack>
#include <unordered_map>
using namespace std;
const int N=100010;

//棧的特性:後進先出
stack<int> value;     //定義一個棧容器:用來儲存int型數值
stack<char> symbol;   //定義一個棧容器:用來儲存char型字元

//unordered_map:使用雜湊表來實現。它透過雜湊函式將鍵對映到表中的位置,
//從而快速訪問、插入和刪除元素。由於雜湊表的特性,unordered_map 不保證元素按照任何特定的順序儲存。
unordered_map<char, int> p{{'+',1},{'-',1},{'*',2},{'/',2}};

void add(){
   //第二個入棧元素賦值
   int a=value.top();
   value.pop();

   //第一個入棧元素
   int b=value.top();
   value.pop();

   //提取符號操作
   char op=symbol.top();
   symbol.pop();

   //進行一個計算操作
   int ans=0;
   if(op=='+') ans=b+a;
   //為什麼是b-a? 這裡就要明白棧的特性了
   if(op=='-') ans=b-a;
   if(op=='*') ans=b*a;
   if(op=='/') ans=b/a;

   //完成操作再次壓入棧中,進行下一步運算
   value.push(ans); 
}

int main(){
   string s;
   cin >> s;
   int len=s.size();
   for(int i=0;i<len;i++){
      //isdigit:C 庫函式 int isdigit(int c) 檢查所傳的字元是否是十進位制數字字元。
      if(isdigit(s[i])){
         int x=0,j=i;
         //這裡使用迴圈就是看看數值字元型是幾位數,畢竟10都佔兩位,所以要迴圈
         while(j<len && isdigit(s[j])){ 
            x=x*10+s[j]-'0';
            j++;
         }
         value.push(x);
         i=j-1;                       
      }else if(s[i]=='('){
         symbol.push(s[i]);
      }else if(s[i]==')'){
         while(symbol.top()!='('){
            add();
         }
         symbol.pop();
      }else {
         if(!symbol.empty() && p[symbol.top()]>=p[s[i]]){ //判斷棧頂元素優先順序大於新運算子
            add();
         }
         symbol.push(s[i]);
      }
   }
   //判斷運算棧是否為空,不為繼續運算
   while(!symbol.empty()) add();
   cout << value.top();
}

相關文章