• 表达式求值(二叉树方法/C++语言描述)(二)


      表达式二叉树节点的数据可能是运算数或运算符,可以使用一个联合体进行存储;同时还需要一个变量来指示存储的是运算数还是运算符,可以采用和栈方法求值中一样的枚举类型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()用来将表达式转换为二叉树,它需要两个栈,一个用来存储运算符,一个用来存储指向子树的指针;用来存储浮点类型的栈仅在求值时使用。由于parseExpression方法需要访问BinaryTreeNode类的私有成员,因此还要在BinaryTreeNode类中声明Calculator类为友元类。

      转换过程与栈方法求值的运算压栈过程基本相同,当遇到运算数时,生成一个新的节点并将它的指针压入节点指针栈中;遇到运算符时比较当前运算符和运算符栈栈顶运算符的优先级,若运算符栈栈顶运算符的优先级较高,则为它生成一个新的节点,并从节点指针栈中弹出两个节点指针,作为新节点的左子树和右子树,随后将这个新节点的指针压入节点指针栈中,将当前运算符压入运算符栈中,否则只将当前运算符压入运算符栈中。最后反复执行上述过程,直至运算符栈为空,节点指针栈的栈顶元素即为指向树根节点的指针。表达式“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类,用于释放已占用的二叉树节点空间,可以防止程序内存溢出。

  • 相关阅读:
    10-JS的函数学习
    Servlet(生命周期)
    09-js数组常用方法
    08-计算器案例
    07-js数组
    06-js的逻辑结构
    使用css设置三角形
    关于background-size 的一点小坑
    a 标签实现分享功能
    关于页面缩放时css错乱的处理方法---之一
  • 原文地址:https://www.cnblogs.com/lets-blu/p/7289643.html
Copyright © 2020-2023  润新知