標籤:指示 代碼 numbers ase png com rac ken union
運算式二叉樹節點的資料可能是運算數或運算子,可以使用一個聯合體進行儲存;同時還需要一個變數來指示儲存的是運算數還是運算子,可以採用和棧方法中一樣的枚舉類型TokenType:
1 typedef enum 2 { 3 BEGIN, 4 NUMBER, 5 OPERATOR, 6 LEFT_BRAC, 7 RIGHT_BRAC 8 } TokenType; 9 10 class Token11 {12 public:13 TokenType _type;14 union15 {16 char op;17 double num;18 } _data;19 };
二叉樹方法的Calculator類則公有繼承自節點資料資料類型為Token類的BinaryTree類:
1 class Calculator : public BinaryTree<Token> 2 { 3 public: 4 void parseExpression(string expression) throw(string); 5 double calculate(); 6 7 private: 8 stack<char> _stkOperators; 9 stack<BinaryTreeNode<Token> *> _stkNodes;10 stack<double> _stkNumbers;11 12 static int priority(char op);13 static double calculate(double d1, char op, double d2) throw(string);14 15 void postOrder(BinaryTreeNode<Token> * node);16 void calculateStack() throw(string);17 void dealWithNumber(char *&pToken) throw(string);18 void dealWithOperator(char *&pToken) throw(string);19 void dealWithLeftBrac(char *&pToken) throw(string);20 void dealWithRightBrac(char *&pToken) throw(string);21 };
方法parseExpression()用來將運算式轉換為二叉樹,它需要兩個棧,一個用來儲存運算子,一個用來儲存指向子樹的指標;用來儲存浮點類型的棧僅在求值時使用。
轉換過程與棧方法的運算壓棧過程基本相同,當遇到運算數時,產生一個新的節點並將它的指標壓入節點指標棧中;遇到運算子時比較當前運算子和運算子棧棧頂運算子的優先順序,若運算子棧棧頂運算子的優先順序較高,則為它產生一個新的節點,並從節點指標棧中彈出兩個節點指標,作為新節點的左子樹和右子樹,最後將這個新節點的指標壓入節點指標棧中,將當前運算子壓入運算子棧中,否則只將當前運算子壓入運算子棧中。最後反覆執行上述過程,直至運算子棧為空白,節點指標棧的棧頂元素即為指向樹根節點的指標。運算式“1+2*3”和“1*2+3”的轉換過程如:
使用代碼描述動作節點指標棧的過程:
1 void Calculator::calculateStack() throw(string) 2 { 3 BinaryTreeNode<Token> * node = new BinaryTreeNode<Token>(); 4 assert(node); 5 node->_data._type = OPERATOR; 6 node->_data._data.op = _stkOperators.top(); 7 _stkOperators.pop(); 8 assert(!_stkNodes.empty()); 9 node->_rightChild = _stkNodes.top();10 _stkNodes.pop();11 assert(!_stkNodes.empty());12 node->_leftChild = _stkNodes.top();13 _stkNodes.pop();14 _stkNodes.push(node);15 }
根據數學規則和最後清空運算子棧的過程,parseExpression()方法實現如下:
1 void Calculator::parseExpression(string expression) throw(string) 2 { 3 destory(); 4 while (!_stkNodes.empty()) 5 { 6 _stkNodes.pop(); 7 } 8 while (!_stkOperators.empty()) 9 {10 _stkOperators.pop();11 }12 TokenType lastToken = BEGIN;13 14 char * pToken = &expression[0];15 while (*pToken)16 {17 switch (lastToken)18 {19 case BEGIN:20 if (*pToken == ‘(‘)21 {22 // an expression begin with a left bracket23 dealWithLeftBrac(pToken);;24 lastToken = LEFT_BRAC;25 }26 else27 {28 // or a number29 dealWithNumber(pToken);30 lastToken = NUMBER;31 }32 break;33 case NUMBER:34 // after a number35 if (*pToken == ‘)‘)36 {37 // it may be a right bracket38 dealWithRightBrac(pToken);39 lastToken = RIGHT_BRAC;40 }41 else42 {43 // it may be an operator44 dealWithOperator(pToken);45 lastToken = OPERATOR;46 }47 break;48 case OPERATOR:49 case LEFT_BRAC:50 // after an operator or a left bracket51 if (*pToken == ‘(‘)52 {53 // it may be a left bracket54 dealWithLeftBrac(pToken);55 lastToken = LEFT_BRAC;56 }57 else58 {59 // it may be a number60 dealWithNumber(pToken);61 lastToken = NUMBER;62 }63 break;64 case RIGHT_BRAC:65 // after a right bracket66 if (*pToken == ‘)‘)67 {68 // it may be another right bracket69 dealWithRightBrac(pToken);70 lastToken = RIGHT_BRAC;71 }72 else73 {74 // it may be an perator75 dealWithOperator(pToken);76 lastToken = OPERATOR;77 }78 break;79 }80 }81 82 while (!_stkOperators.empty())83 {84 if (_stkOperators.top() == ‘(‘)85 {86 throw string("bad token ‘(‘");87 }88 calculateStack();89 }90 91 assert(!_stkNodes.empty());92 _root = _stkNodes.top();93 }
方法實現與棧方法求值的公有方法calculator()基本相同。開始調用的destory()方法繼承自BinaryTree類,用於釋放已佔用的二叉樹節點空間,可以防止程式記憶體溢出。
運算式求值(二叉樹方法/C++語言描述)(二)