HTUT資料結構作業感悟和分析-----C++運用棧解決四則運算
阿新 • • 發佈:2018-12-22
//我只是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;
}
//今天已經週四,(吐舌頭,張老師要求明天就交作業,確實有點晚了
//以後儘量做到週一更新一下
//原理方面我就不再贅述了,以後會說得清楚一點,下面附上我參考過的幾個網頁
// 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;
}