使用棧來檢測運算式括弧的匹配,使用二叉樹來儲存運算的中綴運算式,對錶達式樹進行後序遍曆得到尾碼運算式,運算得到運算式的值。
檢測配對使用棧儲存‘(’符號,當遇到一個‘)’,出棧,若棧空則出錯,運算式結束,棧非空也出錯。
3*5+2構造二叉樹如下
四則運算有優先順序和結合關係,在構造二叉樹時影響構造的過程。優先順序高的運算子深度較深,在後序遍曆時,先遍曆到。
2+3*5構造二叉樹如下
遇到括弧,將括弧中的看成一個子運算式,使用遞迴來解決。
在中綴運算式轉換事的演算法描述如下:
噹噹前的符號是運算數時,檢測當前是否有樹根,沒有則作為樹根,否則作為前一個運算子的右孩子,當根節點沒有右孩子則作為右孩子,有右孩子則作為右孩子的右元算數。如果是+或-,那它就是已知的最後一個運算子,將它作為根節點,原來的樹作為左子樹。如果是*或/,優先順序高,就要和根節點做比較,怎麼改變。如根節點是+或-,則則應該先運算,所以作為右子樹的根節點,原來的子樹作為它的左子樹。如果是同優先順序的,原來的先計算,當前最為根節點,原來作為左子樹。
#ifndef EXCEPTIONS_H#define EXCEPTIONS_H#include <exception>#include <iostream>/** * @brief The OutOfBoundary class 越界異常 */class OutOfBoundary : public std::exception{ friend std::ostream& operator << (std::ostream& os, const std::exception &obj) { os << obj.what(); return os; }public: OutOfBoundary() throw(): exception(){} ~OutOfBoundary() throw(){} const char* what() const throw(){ return "out of boundary!"; }};/** * @brief The BadValue class 壞值異常 */class BadValue : public std::exception{ friend std::ostream& operator << (std::ostream& os, const BadValue &obj) { os << obj.what(); return os; }public: BadValue() throw(): exception(){} ~BadValue() throw(){} const char* what() const throw(){ return "bad value!"; }};class MyException : public std::exception{public: MyException(const char *_mes) throw() : exception(),info(_mes) {} ~MyException() throw(){} const char * what() const throw() { return info.data(); }private: std::string info;};#endif#ifndef BALANCE_HPP#define BALANCE_HPP#include "linkStack.hpp"class Balance{public: enum elemType{OPAREN,CPAREN,OTHER,EOL}; Balance(const char* ex); ~Balance(){} bool balance();private: LinkStack<char> _stack; const char * _ex; elemType getToken();};inlineBalance::Balance(const char *ex) : _ex(ex){}bool Balance::balance(){ elemType t = getToken(); while (t != EOL) { switch (t) { case OPAREN: _stack.push('('); break; case CPAREN: if (_stack.isEmpty()) { return false; } else { _stack.pop(); } break; default: break; } t = getToken(); } return _stack.isEmpty(); \}Balance::elemType Balance::getToken(){ elemType re; while(*_ex == ' ') ++_ex; if (*_ex >= '0' && *_ex <= '9') { while(*_ex >= '0' && *_ex <= '9') { ++_ex; } return OTHER; } switch (*_ex++) { case '(': re = OPAREN; break; case ')': re = CPAREN; break; case '\0': re = EOL; break; default: re = OTHER; break; } return re;}#endif // BALANCE_HPP#ifndef CALCULATOR_HPP#define CALCULATOR_HPP#include "balance.hpp"#include "exceptions.hpp"template <typename T>class Calculator{public: /** * @brief The elemType enum * 數字 加 減 乘 除 開括弧open 閉括弧close 結束標識 */ enum elemType{DATA, ADD, SUB, MULIT, DIV, OPAREN, CPAREN, EOL}; Calculator(char *ex); ~Calculator() { destroy(root); } T result();private: struct node { elemType type; T data; node *lc, *rc; node(elemType _type, T _data, node *_lc = 0, node *_rc = 0): type(_type), data(_data), lc(_lc), rc(_rc) { } }; node *create(char *&ex); elemType getToken(char *&ex, T &value); void destroy(node *r); T result(node *r); node *root;};template <typename T>Calculator<T>::Calculator(char *ex){ Balance b(ex); if (!b.balance()) { throw MyException("() not match!!!"); } root = create(ex);}template <typename T>void Calculator<T>::destroy(node *r){ if (r != NULL) { destroy(r->lc); destroy(r->rc); delete r; }}template <typename T>typename Calculator<T>::node* Calculator<T>::create(char *&ex){ node* p, *root = 0; elemType reType; T value; while(*ex) { reType = getToken(ex,value); switch (reType) { case DATA: case OPAREN: if(reType == DATA) { p = new node(DATA,value); } else { /** 解決這樣情況 (4-2)*5 樹形結構為 |------| |------| | _ | * | _ | |------| ---------------> |------| (X) | \ | \ | \ | \ |------| |-----| |------| |-----| | 4 | | 2 | | 4 | | * | |------| |-----| |------| |-----| | \ | \ |------| |------| |2 | | 5 | |------| |------| ***/ p = create(ex); node *q = new node(DATA,result(p)); destroy(p); p = q; } if (root == NULL) { root = p; } else { if (root->rc == NULL) { root->rc = p; } else { root->rc->rc = p; } } break; case CPAREN: case EOL: return root; case ADD: case SUB: root = new node(reType,0,root); break; case MULIT: case DIV: if (root->type == DATA || root->type == MULIT || root->type == DIV) { root = new node(reType,0,root); } else { root->rc = new node(reType,0,root->rc); } break; default: return NULL; break; } } return root;}template <typename T>typename Calculator<T>::elemType Calculator<T>::getToken(char *&ex, T &value){ char t; while(*ex == ' ') ++ex; if (*ex >= '0' && *ex <= '9') { value = 0; while(*ex >= '0' && *ex <= '9') { value = value*10 + *ex - '0'; ++ex; } return DATA; } if (*ex == '\0') return EOL; t = *ex; ++ex; switch (t) { case '+': return ADD; case '-': return SUB; case '*': return MULIT; case '/': return DIV; case '(': return OPAREN; case ')': return CPAREN; default: return EOL; }}template <typename T>T Calculator<T>::result(){ return result(root);}template <typename T>T Calculator<T>::result(node *r){ elemType t = r->type; T t2,t3; if (t == DATA) { return r->data; } t2 = result(r->lc); t3 = result(r->rc); switch (t) { case ADD: r->data = t2 + t3; break; case SUB: r->data = t2 - t3; break; case MULIT: r->data = t2 * t3; break; case DIV: r->data = t2 / t3; break; default: break; } return r->data;}#endif // CALCULATOR_HPPint main(){ try { Calculator<int> ex("(4-2)*(10+(4+6)/2)+2"); std::cout << ex.result(); } catch(std::exception &ex) { std::cout << ex.what() << std::endl; } return 0;}
修複一個bug,可以處理小數,不過不進行檢錯
template <typename T>typename Calculator<T>::elemType Calculator<T>::getToken(char *&ex, T &value){ char t; while(*ex == ' ') ++ex; if (*ex >= '0' && *ex <= '9') { value = 0; T t = 0; T t2 = 1; bool f = false;//標識小數點 while((*ex >= '0' && *ex <= '9') || *ex == '.' ) { if (*ex == '.') { f = true; ++ex; continue; } if (!f) { value = value*10 + *ex - '0'; } else { t2 *= 0.1; t = t + (*ex - '0')*t2; } ++ex; } value += t; return DATA; } if (*ex == '\0') return EOL; t = *ex; ++ex; switch (t) { case '+': return ADD; case '-': return SUB; case '*': return MULIT; case '/': return DIV; case '(': return OPAREN; case ')': return CPAREN; default: return EOL; } }
修複bug
Balance::elemType Balance::getToken(){ elemType re; while(*_ex == ' ') ++_ex; //這段代碼注釋掉沒有影響,但是會增加函數的調用,效能會受影響 if (*_ex >= '0' && *_ex <= '9') { while((*_ex >= '0' && *_ex <= '9') || *_ex == '.') { ++_ex; } return OTHER; } switch (*_ex++) { case '(': re = OPAREN; break; case ')': re = CPAREN; break; case '\0': re = EOL; break; default://這邊處理所有的不是‘(’‘)’‘\0’的情況,不過函數會返回字串長度+1次 re = OTHER; break; } return re;}template <typename T>Calculator<T>::Calculator(char *ex) : root(0), _ex(ex){ // Balance b(ex); // if (!b.balance()) { // throw MyException("() not match!!!"); // } //在建構函式中爆出異常回導致解構函式不能調用 // root = create(ex);}template <typename T>T Calculator<T>::result(){ Balance b(_ex); if (!b.balance()) { throw MyException("() not match!!!"); } root = create(_ex); return result(root);}
_ex為添加的資料成員