表达式求值(二叉树方法/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()用来将表达式转换为二叉树,它需要两个栈,一个用来存储运算符,一个用来存储指向子树的指针;用来存储浮点类型的栈仅在求值时使用。

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

表达式求值(二叉树方法/C++语言描述)(二)的相关文章

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

二叉树方法求值对运算数处理的方法与栈方法求值不太相同,除了将字符串中的运算数转换为浮点类型外,还需要生成新的节点: 1 void Calculator::dealWithNumber(char *&pToken) throw(string) 2 { 3 if (!isdigit(*pToken) && *pToken != '-') 4 { 5 throw string("bad token '") + *pToken + "'"; 6 }

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

本例中的二叉树图是使用Graphviz绘制的(Graphviz官网),在Ubuntu Linux下可以使用apt-get命令安装它: 1 sudo apt-get install graphviz 表达式"1+2*3"和"1*2+3"的Dot代码如下: 1 # exp1_3.dot 2 digraph G{ 3 1 4 2 5 3 6 "*" -> 2 7 "*" -> 3 8 "+" ->

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

代码清单 1 // binarytree.h 2 #ifndef BINARYTREE_H 3 #define BINARYTREE_H 4 5 template <typename T> class BinaryTree; 6 7 template <typename T> 8 class BinaryTreeNode 9 { 10 public: 11 friend class BinaryTree<T>; 12 friend class Calculator; 1

使用逆波兰式进行表达式求值

中缀表达式及后缀表达式图解中说明了使用逆波兰式进行表达式求值的方法,这里使用C++进行实现.实现和原理讲解有一点不同,需要进一步进行细化. 关于将中缀表达式转换成后后缀表达式的规则: 规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分:若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于找顶符号(乘除优先加减)则栈顶元素依次出找并输出,并将当前符号进栈,一直到最终输出后缀表达式为止. 上面的规则转换成下面的执行规则: 1.遇到操作数:直接输出(添加到后缀

LeetCode:逆波兰表达式求值【150】

LeetCode:逆波兰表达式求值[150] 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除法只保留整数部分. 给定逆波兰表达式总是有效的.换句话说,表达式总会得出有效数值且不存在除数为 0 的情况. 示例 1: 输入: ["2", "1", "+", "3", "*"] 输出: 9 解释: ((2

数据结构算法C语言实现(八)--- 3.2栈的应用举例:迷宫求解与表达式求值

一.简介 迷宫求解:类似图的DFS.具体的算法思路可以参考书上的50.51页,不过书上只说了粗略的算法,实现起来还是有很多细节需要注意.大多数只是给了个抽象的名字,甚至参数类型,返回值也没说的很清楚,所以很多需要自己揣摩.这也体现了算法和程序设计语言的特点,算法更侧重本质的描述,而任何编程语言都要照顾到实现的细节以及数据类型等语法方面的需求. 表达式求值: [编码中....] 二.头文件 迷宫求解: 1 //3_2_maze.h 2 /** 3 author:zhaoyu 4 email:[em

序列点在C语言表达式求值中的作用

摘要: 本文开创性地分析了序列点在C语言表达式求值中的作用:序列点左边的操作数要先于其右边的操作数求值.讨论了逗号操作符,.逻辑与操作符&&.逻辑或操作符||和条件操作符?:的问号处需要序列点的原因.举例分析了序列点在表达式求值中的作用. 关键字:序列点 表达式求值 C语言 C语言作为一种主流程序设计语言,许多编程语言如Java.C++.C#都借鉴了它的语法.C语言也是一种很适当的程序设计入门的教学语言,国内大专院校的许多专业都开设了这门课程并且大多将其作为第一门程序设计语言课程,同时C语

C/C++ 语言中的表达式求值

蛱酝缒 C/C++ 语言中的表达式求值

一起talk C栗子吧(第二十一回:C语言实例--表达式求值)

各位看官们,大家好,前几回中咱们说了堆栈的原理,并且举了实际的例子进行解说,这一回咱们说的例 子是:表达式求值.表达式求值和上一回中说的括号匹配一样,都使用了堆栈的原理,大家可以从例子中 看出来,所以我们把它们放在一起.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们,我们在这里说的表达式为包含加,减,乘除的四则运算表达式.例如:1+2*3-4/5就是一个四则运 算表达式.这个表达式中,运算符在数字中间,所以我们叫它中缀表达式,这也是符合我们思维的一种表 现形式,不过,计算机就不理解中