一、前言
本文主要記錄筆者在學習C++適配器相關內容時解決的一道練習題,亦是有拋磚引玉之意~
二、正文
一般我們常用的數學表達式形式,像(a+b)*c等把加減乘除等運算符放在中間的稱呼爲中綴表達式。
但是中綴表達式的運算順序受括號影響很大,那有沒有可以無視的括號的表達形式呢?答案是肯定的,那就是逆波蘭式,也叫後綴表達式。
引用百度百科的定義:
一個表達式E的後綴形式可以如下定義:
(1)如果E是一個變量或常量,則E的後綴式是E本身。
(2)如果E是E1 op E2形式的表達式,這裏op是任何二元操作符,則E的後綴式爲E1’E2’ op,這裏E1’和E2’分別爲E1和E2的後綴式。
(3)如果E是(E1)形式的表達式,則E1的後綴式就是E的後綴式。
(a + b ) * c的後綴表達式爲 a b + c *,具體轉換算法描述參考逆波蘭表達式
三、算法實現
引用Linux 的創始人 Linus Torvalds的一句名言:Talk is cheap. Show me the code.
下面直接呈上筆者寫的拙劣的算法實現:
1.主函數
//比較運算符優先級
int cpapriority(const string &a, const string &b);
//處理表達式
vector<string> myrpn(istream &in,ostream &out);
int main(){
for (auto st : myrpn(cin, cout)) {
cout << st << endl;
std::istringstream istrm(st);
string s;
long double result = 0.0; //保存最終運算結果
std::stack<long double> stk;
while (istrm >> s) {
if (s.find_first_not_of("+-*/") != string::npos) {
stk.push(std::stod(s));
}
else
{
auto lval = stk.top(); stk.pop();
auto rval = stk.top(); stk.pop();
if (s.find('+') != string::npos) {
result = lval + rval;
stk.push(result);
}
else if (s.find('-') != string::npos) {
result = rval - lval;
stk.push(result);
}
else if (s.find('*') != string::npos) {
result = lval * rval;
stk.push(result);
}
else if (s.find('/') != string::npos) {
if (rval==0)
{
cout << "Divisor can't be zero" << endl;
return EXIT_FAILURE;
}
//後一個數除以前一個數
result = rval / lval;
stk.push(result);
}
}
}
cout << stk.top() << endl;
}
return EXIT_SUCCESS; //cstdlib定義的預處理變量,與機器無關。其中EXIT_FAILURE表示失敗
}
2.將中綴表達式轉換爲後綴表達式
vector<string> myrpn(istream &in,ostream &out) {
//先將中綴表達式轉換爲後綴表達式(逆波蘭式)
std::stack<string> opstk; //存放操作符的棧
//opstk.push("#");
std::stack<string> valstk; //存放操作數
vector<string> results; //存放後綴表達式的結果
string expression;
//windows按ctrl+z結束輸入,unix/linux按ctrl+d
while (in >> expression) {
//(輸入表達式) 方便後面求解
expression.insert(0, "(");
expression.push_back(')');
decltype(expression.size()) index = 0;
//用於保存多位數
string val;
val.clear();
try {
while (index != expression.size())
{
auto ch = expression[index];
auto s = string(1, ch);
if (ch == '(') {
//左括號直接入棧
opstk.push(s);
}
else if (ch == ')') {
if (!val.empty()) {
valstk.push(val);
val.clear();
}
while (opstk.top() != string(1, '('))
{
valstk.push(opstk.top());
opstk.pop();
}
opstk.pop(); //彈出左括號
}
else if (s.find_first_of("0123456789")
!= string::npos) {
val += ch;
}
else if (s.find_first_of("+-*/")
!= string::npos) {
if (!val.empty()) {
//檢測到運算符說明操作數處理完畢
//此處爲了避')'和運算符'+-*/'相鄰而將操作數入棧兩次
valstk.push(val);
val.clear();
}
val.clear();
while (cpapriority(opstk.top(), s) == 0) {
valstk.push(opstk.top());
opstk.pop();
}
opstk.push(s);
}
else {
throw - 1; //出現非法符號
}
++index;
}
//輸出結果是逆序的,需要處理下
string result;
while (!valstk.empty())
{
//不斷在頭部插入,加入空格便於區分
result.insert(0, valstk.top()+" ");
valstk.pop();
}
results.push_back(result); //保存轉換結果
}
catch (int errcode) {
out << "Errorcode:" << errcode
<< " invalid char";
}
catch (...) {
out << "Unknown Error";
}
}
return results;
}
3.比較運算符優先級
int cpapriority(const string &a, const string &b) {
//1代表優先級比存儲操作符的棧的棧頂元素優先級高,可直接入棧
//0代表優先級相同或低,需出棧
if (a=="(")
{
return 1;
}
else if (a.find_first_of("+-")
!= string::npos) {
if (b.find_first_of("*/")
!= string::npos) {
return 1;
}
else
{
return 0;
}
}
//else if (a.find_first_of("*/")) {
else
{
return 0;
}
}
4.測試結果
以下結果僅在windows 10 ,vs2017環境下測試:
四、寫在最後
以上就是本文的全部內容啦,感謝您的閱讀。若有幫助,煩請點個贊吖👍~