表达式二叉树节点的数据可能是运算数或运算符,可以使用一个联合体进行存储;同时还需要一个变量来指示存储的是运算数还是运算符,可以采用和栈方法求值中一样的枚举类型TokenType:

 typedef enum
{
BEGIN,
NUMBER,
OPERATOR,
LEFT_BRAC,
RIGHT_BRAC
} TokenType; class Token
{
public:
TokenType _type;
union
{
char op;
double num;
} _data;
};

  二叉树方法求值的Calculator类公有继承自节点数据数据类型为Token类的BinaryTree类:

 class Calculator : public BinaryTree<Token>
{
public:
void parseExpression(string expression) throw(string);
double calculate(); private:
stack<char> _stkOperators;
stack<BinaryTreeNode<Token> *> _stkNodes;
stack<double> _stkNumbers; static int priority(char op);
static double calculate(double d1, char op, double d2) throw(string); void postOrder(BinaryTreeNode<Token> * node);
void calculateStack() throw(string);
void dealWithNumber(char *&pToken) throw(string);
void dealWithOperator(char *&pToken) throw(string);
void dealWithLeftBrac(char *&pToken) throw(string);
void dealWithRightBrac(char *&pToken) throw(string);
};

方法parseExpression()用来将表达式转换为二叉树,它需要两个栈,一个用来存储运算符,一个用来存储指向子树的指针;用来存储浮点类型的栈仅在求值时使用。由于parseExpression方法需要访问BinaryTreeNode类的私有成员,因此还要在BinaryTreeNode类中声明Calculator类为友元类。

  转换过程与栈方法求值的运算压栈过程基本相同,当遇到运算数时,生成一个新的节点并将它的指针压入节点指针栈中;遇到运算符时比较当前运算符和运算符栈栈顶运算符的优先级,若运算符栈栈顶运算符的优先级较高,则为它生成一个新的节点,并从节点指针栈中弹出两个节点指针,作为新节点的左子树和右子树,随后将这个新节点的指针压入节点指针栈中,将当前运算符压入运算符栈中,否则只将当前运算符压入运算符栈中。最后反复执行上述过程,直至运算符栈为空,节点指针栈的栈顶元素即为指向树根节点的指针。表达式“1+2*3”和“1*2+3”的转换过程如下图:

使用代码描述操作节点指针栈的过程:

 void Calculator::calculateStack() throw(string)
{
BinaryTreeNode<Token> * node = new BinaryTreeNode<Token>();
assert(node);
node->_data._type = OPERATOR;
node->_data._data.op = _stkOperators.top();
_stkOperators.pop();
assert(!_stkNodes.empty());
node->_rightChild = _stkNodes.top();
_stkNodes.pop();
assert(!_stkNodes.empty());
node->_leftChild = _stkNodes.top();
_stkNodes.pop();
_stkNodes.push(node);
}

  根据数学规则以及最后清空运算符栈的过程,parseExpression()方法实现如下:

 void Calculator::parseExpression(string expression) throw(string)
{
destory();
while (!_stkNodes.empty())
{
_stkNodes.pop();
}
while (!_stkOperators.empty())
{
_stkOperators.pop();
}
TokenType lastToken = BEGIN; char * pToken = &expression[];
while (*pToken)
{
switch (lastToken)
{
case BEGIN:
if (*pToken == '(')
{
// an expression begin with a left bracket
dealWithLeftBrac(pToken);;
lastToken = LEFT_BRAC;
}
else
{
// or a number
dealWithNumber(pToken);
lastToken = NUMBER;
}
break;
case NUMBER:
// after a number
if (*pToken == ')')
{
// it may be a right bracket
dealWithRightBrac(pToken);
lastToken = RIGHT_BRAC;
}
else
{
// it may be an operator
dealWithOperator(pToken);
lastToken = OPERATOR;
}
break;
case OPERATOR:
case LEFT_BRAC:
// after an operator or a left bracket
if (*pToken == '(')
{
// it may be a left bracket
dealWithLeftBrac(pToken);
lastToken = LEFT_BRAC;
}
else
{
// it may be a number
dealWithNumber(pToken);
lastToken = NUMBER;
}
break;
case RIGHT_BRAC:
// after a right bracket
if (*pToken == ')')
{
// it may be another right bracket
dealWithRightBrac(pToken);
lastToken = RIGHT_BRAC;
}
else
{
// it may be an perator
dealWithOperator(pToken);
lastToken = OPERATOR;
}
break;
}
} while (!_stkOperators.empty())
{
if (_stkOperators.top() == '(')
{
throw string("bad token '('");
}
calculateStack();
} assert(!_stkNodes.empty());
_root = _stkNodes.top();
}

方法实现与栈方法求值的公有方法calculator()基本相同。开始调用的destory()方法继承自BinaryTree类,用于释放已占用的二叉树节点空间,可以防止程序内存溢出。

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

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

    使用二叉树对算数表达式(以下简称为表达式)进行求值,实质上是将表达式转换为二叉树,对其进行后序遍历,得到后缀表达式的同时可以求得表达式的值.转换和求值的过程也需要借助数据结构栈的帮助. 二叉树数据结构 ...

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

    二叉树方法求值对运算数处理的方法与栈方法求值不太相同,除了将字符串中的运算数转换为浮点类型外,还需要生成新的节点: void Calculator::dealWithNumber(char *& ...

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

    本例中的二叉树图是使用Graphviz绘制的(Graphviz官网),在Ubuntu Linux下可以使用apt-get命令安装它: sudo apt-get install graphviz 表达式 ...

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

    代码清单 // binarytree.h #ifndef BINARYTREE_H #define BINARYTREE_H template <typename T> class Bin ...

  5. 表达式求值--数据结构C语言算法实现

    这篇博客介绍的表达式求值是用C语言实现的,只使用了c++里面的引用. 数据结构课本上的一个例题,但是看起来很简单,实现却遇到了很多问题. 这个题需要构建两个栈,一个用来存储运算符OPTR, 一个用来存 ...

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

    LeetCode:逆波兰表达式求值[150] 题目描述 根据逆波兰表示法,求表达式的值. 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 说明: 整数除 ...

  7. 表达式求值(栈方法/C++语言描述)(二)

    上篇中完成了对表达式求值的整体过程,接下来看看如何处理不同类型的token. 对运算数的处理比较简单,它直接调用函数strtod(),将字符串中的运算数转换为浮点类型并将它压入运算数栈中: void ...

  8. 利用栈实现算术表达式求值(Java语言描述)

    利用栈实现算术表达式求值(Java语言描述) 算术表达式求值是栈的典型应用,自己写栈,实现Java栈算术表达式求值,涉及栈,编译原理方面的知识.声明:部分代码参考自茫茫大海的专栏. 链栈的实现: pa ...

  9. Java描述表达式求值的两种解法:双栈结构和二叉树

    Java描述表达式求值的两种解法:双栈结构和二叉树 原题大意:表达式求值 求一个非负整数四则混合运算且含嵌套括号表达式的值.如: # 输入: 1+2*(6/2)-4 # 输出: 3.0 数据保证: 保 ...

随机推荐

  1. tomcat抬头有“选择”或“选定”,导致tomcat无法运行问题

    2. 遇到tomcat抬头有"选择"或"选定",导致tomcat无法运行问题 解决:在tomcat抬头右键--属性,去掉"快速编辑模式"勾选 ...

  2. html与xhtml有什么区别?

    HTML与XHTML之间的差别,主要分为功能上的差别和书写习惯的差别两方面. 关于功能上的差别,主要是XHTML可兼容各大浏览器.手机以及PDA,并且浏览器也能快速正确地编译网页. 由于XHTML的语 ...

  3. 【解决】安装compass失败(gem install compass)

    原始日期:2016-01-25 16:26 这个问题比较常见.   很多人在安装ruby后再使用gem install compass命令安装compass,发现安装失败.     [解决方法:] / ...

  4. 通过 pxe(网络安装)完成centos 系统的网络安装

    首先交代环境.本地2台主机,一台windows主机,一台等待安装centos的主机.2台主机在同一个局域网.通过路由器自动获取ip上网. 网上大多数pxe安装方式都采用自己搭建dns服务器的方式来进行 ...

  5. JavaScript 基础——使用js的三种方式,js中的变量,js中的输出语句,js中的运算符;js中的分支结构

    JavaScript 1.是什么:基于浏览器 基于(面向)对象 事件驱动 脚本语言 2.作用:表单验证,减轻服务器压力 添加野面动画效果 动态更改页面内容 Ajax网络请求 () 3.组成部分:ECM ...

  6. ASP.NET Core 源码学习之 Options[1]:Configure

    配置的本质就是字符串的键值对,但是对于面向对象语言来说,能使用强类型的配置是何等的爽哉! 目录 ASP.NET Core 配置系统 强类型的 Options Configure 方法 源码解析 ASP ...

  7. CSS(3)实现水平垂直居中效果

    CSS实现水平垂直居中对齐 在CSS中实现水平居中,会比较简单.常见的,如果想实现inline元素或者inline-block元素水平居中,可以在其父级块级元素上设置text-align: cente ...

  8. js中的匿名函数自执行

    随笔,java中因为有修饰符的存在,那就有private类的存在,js不一样,没有修饰词一说,因此为了防止全局变量的污染,js中就出现了匿名函数,直接上code,看到的人可以自己体会: (functi ...

  9. linux软件包介绍

    一. 软件包的种类 源码包 二进制包(rpm包.系统默认包) 二. 优缺点对比 源码包 源码包的优点 1) 开源,源码可见,且可以修改 2) 配置更加灵活,可以自由选择所需的功能 3) 软件是编译安装 ...

  10. 搭建开源java博客并通过域名访问

    这个博客系统是王爵在GitHub上开源的,通过简单几步就可以部署成功. 前面几步可以参照如下几个链接: 1.https://www.qcloud.com/community/article/29008 ...