表达式二叉树节点的数据可能是运算数或运算符,可以使用一个联合体进行存储;同时还需要一个变量来指示存储的是运算数还是运算符,可以采用和栈方法中一样的枚举类型TokenType:
1 typedef enum 2 { 3 BEGIN, 4 NUMBER, 5 OPERATOR, 6 LEFT_BRAC, 7 RIGHT_BRAC 8 } TokenType; 9 10 class Token 11 { 12 public: 13 TokenType _type; 14 union 15 { 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 bracket 23 dealWithLeftBrac(pToken);; 24 lastToken = LEFT_BRAC; 25 } 26 else 27 { 28 // or a number 29 dealWithNumber(pToken); 30 lastToken = NUMBER; 31 } 32 break; 33 case NUMBER: 34 // after a number 35 if (*pToken == ‘)‘) 36 { 37 // it may be a right bracket 38 dealWithRightBrac(pToken); 39 lastToken = RIGHT_BRAC; 40 } 41 else 42 { 43 // it may be an operator 44 dealWithOperator(pToken); 45 lastToken = OPERATOR; 46 } 47 break; 48 case OPERATOR: 49 case LEFT_BRAC: 50 // after an operator or a left bracket 51 if (*pToken == ‘(‘) 52 { 53 // it may be a left bracket 54 dealWithLeftBrac(pToken); 55 lastToken = LEFT_BRAC; 56 } 57 else 58 { 59 // it may be a number 60 dealWithNumber(pToken); 61 lastToken = NUMBER; 62 } 63 break; 64 case RIGHT_BRAC: 65 // after a right bracket 66 if (*pToken == ‘)‘) 67 { 68 // it may be another right bracket 69 dealWithRightBrac(pToken); 70 lastToken = RIGHT_BRAC; 71 } 72 else 73 { 74 // it may be an perator 75 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类,用于释放已占用的二叉树节点空间,可以防止程序内存溢出。
时间: 2024-10-12 17:32:48