假定給定一個四則運算字符串:8+7*2 –9/3;那麼該表達式的值爲19. 但是如何用C/C++程序上實現計算一個字符串表達式呢?一個常用的做法是將表達式的字符串轉換爲後綴表達式,即:1)表達式8+2,轉換爲後綴表達式是8 2+;2)表達式8 +3*4,轉換爲後綴表達式是834*+;
然後通過棧的操作可以求得它們的結果:
以2)例爲例:
壓入8,
壓入3,
壓入4,
遇到*,
然後彈出兩個字符,即3和4,進行*的運算,得到12
壓入12;
遇到+
彈出兩個字符,8和12,進行+的運算,得到20;
此時到達後綴表達式的末尾,因此最終的結果是20.
上面考慮的是簡單的情況,比較複雜的情況是:
1、表達式中包含浮點型的數據以及負數
2、表達式中包含“(”和“)”的括號操作
不過以上兩種情況都有可以解決的方法:
對於情況1,可以先對字符串表達式進行處理,即定義一個string類的鏈表,將每個可以作爲合法的子串存儲到鏈表上,即比如:-8 + 3.77*2.5 + -3;那麼將它轉換爲的鏈表就是:
-8 |
+ |
3.77 |
* |
2.5 |
+ |
-3 |
然後再將其轉換爲後綴表達式就可以了。
對於情況2 主需要在轉換爲後綴表達式時,注意下括號的影響和去除就可以了。
那麼就總結下字符串表達式求值的方法:
1、 分析字符串中是否存在負數和浮點數,然後將他們依次存入一個叫做substring的鏈表中,如上面所示;
2、 然後再利用一個list<stirng>存儲將要生成的後綴表達式,利用一個stack<string >存儲操作符;具體方法如下
1)、遍歷substring的鏈表,如果是操作數直接加入list<string>;如果是操作符,則比較其和前一個操作符的優先級,如果後來的操作符的優先級高於或等於前者,則直接壓入棧,如果後來的操作如的優先級低於前者,則彈出前者,並將前者加入list<string>中去。
2)、如果遇到“(”,則將其直接壓入操作符棧,以後的的操作符同上處理,知道遇到“)”,則依次彈出“(”和“)”之間的內容。後面依據重複第一步。
3、計算後綴表達式
需要一個棧,即依次遍歷後綴表達式的鏈表list<string>;如果遇到操作數,則直接壓棧;如果遇到操作符,則彈出兩個操作數,並進行相應的計算;然後將計算結果壓入棧;繼續遍歷,直到到達鏈表的末尾。
舉一個例子,能更清晰的瞭解其中的過程:
假設字符串表達式是 -7*(8+7)-6*2 + -8;那麼它的substring鏈表是
-7 |
* |
( |
8 |
+ |
7 |
) |
- |
6 |
* |
2 |
+ |
-8 |
那麼對其做轉換爲後綴表達式的處理:(紅色部分代表後綴表達式的鏈表,藍色代表操作符棧)
第一步:將“-7”放入後綴表達式鏈表,“*”壓入棧;
第二步:遇到“(”,將其壓入棧,並將“8”放入後綴表達式鏈表;
第三步:遇到“+”,將其壓入棧,並將“7”放入後綴表達式鏈表;
第四步:遇到“)”,將棧中的操作符依次放入後綴表達式鏈表;此時的後綴表達式是:-787+
第五步:遇到“-”,由於“-”的優先級小於之前的“*”,因此“*”退棧並放入後綴表達式鏈表;然後將“-”壓入棧;將“6”放入後綴表達式鏈表;此時的後綴表達式是:-787+*6
第六步:遇到“*”,由於“*”的優先級大於“-”,將其壓入棧,並將“2”放入後綴表達式鏈表;
第七步:遇到“+”,由於“+”的優先級小於“*”,因此類似第五步,得到的後綴表達式是:
-787+*62*-8+-
下面編寫C++代碼實現字符串的四則運算,在這裏假設字符串裏出現的都是正整數,即沒有負數和浮點數,因此忽略的了第一步的有關substring鏈表的計算。
#include <iostream>
using namespace std;
#include <stack>
#include <list>
#include <cstring>
#include <queue>
queue<string> substring_queue;
list<string> suffix_list;
stack<string> cal_stack;
void create_substring_list(char *pch)
{
if(NULL == pch)
{
cout<<"str is NULL!"<<endl;
return;
}
list<string> substring_list; //
while('\0' != *pch)
{
string str="";
str += *pch++;
substring_list.push_back(str);
}
list<string>::iterator iter = substring_list.begin();
while(iter != substring_list.end())
{
substring_queue.push(*iter);
++iter;
}
}
void create_suffix_list(queue<string> que)
{
if(que.empty())
return;
string str_temp;
while(!que.empty())
{
str_temp = que.front();
if(str_temp == "(")
{
cal_stack.push(str_temp);
}
else if(str_temp == ")")
{
while(!cal_stack.empty()&&cal_stack.top()!="(")
{
suffix_list.push_back(cal_stack.top());
cal_stack.pop();
}
if(!cal_stack.empty())
cal_stack.pop();
}
else if(str_temp == "+" ||str_temp == "-")
{
if(cal_stack.empty() || cal_stack.top() == "(")
cal_stack.push(str_temp);
else
{
while(!cal_stack.empty())
{
suffix_list.push_back(cal_stack.top());
cal_stack.pop();
}
cal_stack.push(str_temp);
}
}
else if(str_temp == "*" || str_temp == "/")
{
cal_stack.push(str_temp);
}
else
{
suffix_list.push_back(str_temp);
}
que.pop();
}
while(!cal_stack.empty())
{
suffix_list.push_back(cal_stack.top());
cal_stack.pop();
}
}
void calculate(list<string> lst)
{
if(lst.empty())
return ;
int num,n1,n2;
stack<int> int_stack;
char ch;
list<string>::iterator iter = lst.begin();
while(iter != lst.end())
{
ch = (*iter).at(0);
//cout<<ch<<endl;
switch(ch)
{
case '+':
n1 = int_stack.top();
int_stack.pop();
n2 = int_stack.top();
int_stack.pop();
num = n1 +n2;
int_stack.push(num);
cout<<"+ num "<<num<<endl;
break;
case '-':
n1 = int_stack.top();
int_stack.pop();
n2 = int_stack.top();
int_stack.pop();
num = n2-n1;
int_stack.push(num);
cout<<"+ num "<<num<<endl;
break;
case '*':
n1 = int_stack.top();
int_stack.pop();
n2 = int_stack.top();
int_stack.pop();
num = n1*n2;
int_stack.push(num);
cout<<"+ num "<<num<<endl;
break;
case '/':
n1 = int_stack.top();
int_stack.pop();
n2 = int_stack.top();
int_stack.pop();
num = n2/n1;
int_stack.push(num);
cout<<"+ num "<<num<<endl;
break;
default:
num = ch-'0';
cout<<num<<" shuzi"<<endl;
int_stack.push(num);
break;
}
++iter;
}
cout<<int_stack.top()<<endl;
}
int main()
{
char *pch = "8+7*2-(9+1)";
create_substring_list(pch);
/*while(!substring_queue.empty())
{
string str = substring_queue.front();
cout<<str.at(0);
substring_queue.pop();
}
cout<<endl;*/
create_suffix_list(substring_queue);
list<string>::iterator iter = suffix_list.begin();
while(iter != suffix_list.end())
{
cout<<*iter;
++iter;
}
cout<<endl;
calculate(suffix_list);
return 0;
}
以上代碼有些冗雜,是因爲我想多用幾個STL中的數據類型,其實裏面很多的都是不必要的操作,比如create_substring_list()函數中:首先將字符串的內容,賦給一個鏈表,然後再把鏈表的內容賦給一個隊列;其實這裏面的鏈表是多餘的,完全可以直接把字符串的內容賦給隊列。還有就是代碼裏面的stack,list和queue都是用string類型實例化的,其實在這裏只考慮了簡單的0~9的數字的四則運算(不包括浮點和負數),因此完全可以用char類型實例化它們的。這裏用string類型只不過是爲了以後能處理浮點和負數等類型做準備的。而且還學習下如果把一個char字符轉換爲string類型:如:char ch = ‘a’; string str; str+= ch;就實現了把char字符轉換爲string類型了。或者也可以先把char轉換爲char[],然後將char[]賦給string。