1. 程式人生 > >HTUT資料結構作業感悟和分析-----C++運用棧解決四則運算

HTUT資料結構作業感悟和分析-----C++運用棧解決四則運算

//我只是HFUT的一個普通學生,希望把自己學習感悟更新上來
//今天已經週四,(吐舌頭,張老師要求明天就交作業,確實有點晚了
//以後儘量做到週一更新一下
//原理方面我就不再贅述了,以後會說得清楚一點,下面附上我參考過的幾個網頁
// http://blog.csdn.net/summerxiachen/article/details/77073320
// http://blog.csdn.net/qq_34992845/article/details/70313588 //下面的程式碼能夠實現帶括號四則運算,並且處理了一位以上的整數 //感興趣的同學,可以搜尋一下字首(波蘭)、中綴和字尾(逆波蘭)表示式
//另外二叉樹也可以解決四則運算,稍後我會進行更新
//資料結構很難啃,大家加油
#include<iostream>
using namespace std; //下面是我自己寫的一個棧,相比於STL模板庫的棧,pop()函式不僅可以刪除棧頂元素,還可以將棧頂元素的值返回
template<class T>
class Stack{
 public:
  class Node{ //使用了鏈棧來實現,不懂連結串列的同學看一下課本,這裡使用一個內部類表示每個節點
   public:
    T value;//用來儲存當前節點的值
    Node* prev = NULL;//用來儲存前一個數據的地址
  };
  int length = 0;//棧的大小,(此處我的devc++報了一個warning,不過我懶得管了)
  Node* tail = NULL;//用來記錄棧頂元素的地址
  Node* p = NULL;//用來作為一箇中間的儲存變數,沒有實際意義,讀者可以自行體會   void push(T val){//入棧操作
   p = new Node;//用new開一個新空間,並用p記錄其地址
   p->value = val;//不用多說。將val的值儲存
   p->prev = tail;//tail指向當前的棧頂元素,而p指向待更新的棧頂元素,所以p的前一個元素肯定是當前的棧頂元素啦
   tail = p;//前面說了,p只是一箇中間的儲存變數,所以最後還要更新tail的值,更新tail為p
   length++;//棧的大小變大
  }
  int size(){//得到棧的大小
   return length;
  }
  T pop(){//返回當前棧頂元素,並刪除
   T value = tail->value;//記錄需要返回的value的值,不然一會就delete釋放了
   p = tail;//用中間變數p儲存tail
   tail = tail->prev;//將tail的地址更新為tail的前一個的地址
   delete p;//現在棧頂空間已經沒有用處了,所以釋放掉
   length--;//棧的大小變小
   return value;
  }
  T top(){//僅返回當前棧頂元素,但不刪除
   return tail->value;
  }
  bool empty(){//判斷當前棧是否為空
   return length == 0;
  }
};
//函式prototype寫在前面,函式實現在主函式之後,前面進介紹功能,不明白的話,後面還有詳細的解釋
bool isNum(char);//判斷一個字元是不是數字
   
bool comparison(char, char);//比較兩個字元的大小(是按照優先順序比較的),如果前一個字元大於等於後一個字元,為真
   
int translation(char);//這個函式是comparison的輔助函式,將'('表示為0 ,'+'和'-'均為1,'*'和'/'均為2
int operation(char, int, int);//這個是用來運算的,返回運算結果,第一個是運算子,後兩個是運算數
int addition(int, int);//加法
 
int subtraction(int, int);//減法
 
int multiplication(int, int);//乘法
 
int division(int, int);//除法
int main(){
 Stack<int> s_num;//數字棧
 Stack<char> s_oper;//符號棧
 bool isnum = false;//用來儲存當前字元的上一個字元是不是數字,以此來解決一位以上整數的問題
 //附上一個測試案例,答案算出來是3
 string str = "(9-3*2)-7*2/1-((2-3+1*4)-5)*(2*12+2)/4+1";  
 
    for(int i = 0; i < str.length(); i++) {//遍歷字串的每個字元
     if(isNum(str[i])) {//如果是數字
      if(isnum){//如果當前數字字元的上一個字元是數字,就出棧乘10移位再加上當前的數字字元的數值,然後再入棧
       s_num.push(s_num.pop() * 10 + (str[i] - '0'));
   }else{//上一個不是數字,就直接入棧了
    s_num.push(str[i] - '0');
    isnum = true;//別忘了修改isnum的值
   } 
     }else { //如果是符號          
   if(str[i] == ')'){//重點!!字元只分為兩種,不可以入棧的(即')'),和可以入棧的(其他符號)
    while(s_oper.top() != '(') {//一直向前掃描,直到尋找到'('
//下面這個式子有點裝逼,要注意到,兩個pop()函式,是先算右邊的,後算左邊的額,這和編譯原理有關係
//如果不明白,我將等價的式子寫在下面
//int num1 = s_num.pop();  
//int num2 = s_num.pop();
//int result = operation(s_oper.pop(), num2, num1);
//s_num.push(result);
     s_num.push(operation(s_oper.pop(), s_num.pop(), s_num.pop()));
       }
       s_oper.pop();//別忘了刪除'('
   }else{//可以入棧的,又分為可以直接入棧的,和需要進行一些操作再入棧的
//但無論如何,可不可以直接入棧,最後都要入棧,所以在if之後有一個入棧語句
//可以直接的入棧的,有三種情況
//1. 當前棧空 2.當前符號為'(' 3.當前符號的優先順序大於棧頂符號(這也是為什麼'('優先順序定為0,避免出現意外出棧的情況)
    if(!s_oper.empty() && str[i] != '(' && !comparison(str[i], s_oper.top())){//過濾掉以上三種情況
     while(!s_oper.empty() && !comparison(str[i], s_oper.top())){
//棧不能空,不然怎麼比較;噹噹前符號優先順序大於棧頂的符號時,就要停止
      s_num.push(operation(s_oper.pop(), s_num.pop(), s_num.pop()));//原理同上
     }
    }
    s_oper.push(str[i]);
   }
   isnum = false;//別忘了修改isnum的值
     }
    }
  
    while(!s_oper.empty()) {//如果遍歷完了,符號棧還有元素,那麼就需要出棧全部運算了才可以
  s_num.push(operation(s_oper.pop(), s_num.pop(), s_num.pop()));
    }
    cout << s_num.pop();//輸出結果
   
    return 0;
}
    
bool isNum(char char_0) {//判斷一個字元是不是數字
    if(char_0 >= '0' && char_0 <= '9') {
     return true;
    }
    return false;

bool comparison(char char_1, char char_2) {//比較函式
    if(translation(char_1) > translation(char_2)) {
     return true;
    }
    return false;
}
 
int translation(char char_0) {//翻譯函式,將符號用數字表示
    switch(char_0) {
     case '(':
   return 0;
   
     case '+':
      return 1;
        
     case '-':
      return 1;
      
     case '*':
      return 2;
      
     case '/':
      return 2;
  
     default:
      return 0;
    }
}
int operation(char oper, int num_1, int num_2){//運算函式,分為四個子函式
    switch(oper){
     case '+':
      return addition(num_1, num_2);
     
     case '-':
      return subtraction(num_1, num_2);
      
     case '*':
      return multiplication(num_1, num_2);
      
     case '/':
      return division(num_1, num_2);
         
     default:
      return 0;
 }
}
int addition(int num_1, int num_2){
 return num_1 + num_2;
}
 
int subtraction(int num_1, int num_2){
 return num_1 - num_2;
}
 
int multiplication(int num_1, int num_2){
 return num_1 * num_2;
}
 
int division(int num_1, int num_2){
 return num_1 / num_2;
}