算术表达式树

(数据结构)简单算术表达式二叉树的构建和求值 简单算术表达式二叉树的构建和求值 阅读详情

一个面向对象范例(算术表达式树)

1.     问题描述

用程序来表示算术表达式。例如表达式(-5)*(3+4)对应的树如下:

该表达式树包括常数、一元运算符和二元运算符的节点。编写合适的函数来创建这样的树,然后打印该树的完整括号形式。

2.     分析

考虑定义一系列的类,用继承组织起来。这些类有一些共同的点:每个类都要存储一个值以及一些子节点。图中有三种节点:一种表示整数表达式,包含一个整数值,无子节点。另外两种分别表示一元表达式和二元表达式,包含一个操作符,分别有一个或两个子节点。各个节点之间是相互独立的。所以需要定义一个表示节点的基类。如下:

Expr.h

#ifndef   _EXPR_H

#define   _EXPR_H  

#include <iostream>

#include <string>

using namespace std;

namespace Meditation

{

    //公共基类,表示节点

 class Expr_node

 {

    friend ostream& operator<<(ostream &,const Expr_node &);

 public:

     virtual void print(ostream&) const =0;

     virtual ~Expr_node(){ }

 };

 

//整数表达式节点

 class Int_node:public Expr_node

 {

     friend class Expr;

     int n;

     Int_node(int k):n(k) {}

     void print(ostream & o) const

     {

        o<<n;

     }

 };

//一元表达式节点

 class Unary_node:public Expr_node

 {

     friend class Expr;

     string op;

     Expr_node * opnd;

     Unary_node(const string &a,Expr_node *b):op(a),opnd(b)

     { }

     void print(ostream &o)const

     {

        o<<"("<<op<<*opnd<<")";

     }

 

 };

 

 //二元表达式节点

 class Binary_node:public Expr_node

 {

     friend class Expr;

     string op;

     Expr_node *left;

     Expr_node *right;

     Binary_node(const string &a,Expr_node *b,

        Expr_node *c):op(a),left(b),right(c)

     { }

     void print(ostream &o)const

     {

        o<<"("<<*left<<op<<*right<<")";

     }

 };

}

#endif


 

Expr.cpp

#include "expr.h"

using namespace Meditation;

ostream& operator<<(ostream & o,const Expr_node & e)

{

    e.print(o);

    return o;

}


 

上面的方法,有一个问题。用户处理的不是值而是指针,所以必须记住分配和释放对象。动态分配:

Binary_node *t=new Binary_node(“*”,new Unary_node(“-”,new Int_node(5)),

                           new Binary_node(“+”,new new Int_node(3),

 new Int_node(4)));

 

这里,我们必须记住删除这些节点,但是我们不再拥有指向内层new调用所构造出来的对象的指针了!如果通过Binary_node和Unary_node的析构函数删除它们的操作数,也不行,因为可能会多次删除对象,可能有不止一个Expr_node指向同一个下层的表达式树对象。

3.     句柄类

在Expr_node类族中,仅仅表示了图中的圆圈,而没有对箭头建模,正是因为这里把箭头简单的描述成指针,这些用户需要亲自操作指针。所以,类Expr应该是一种句柄类,表示一个边或者说该树结构根源一个边。由于用户关心的是树而不是树种的单个节点,可以用Expr来隐藏Expr_node的继承层次。在Expr中通过构造函数创建三种Expr_node,并且将这个对象的地址存储在创建中的Expr对象中。Expr类的用户就不会直接看到Expr_node 对象了。

 

class Expr {

    friend class Expr_node;

    friend ostream& operator<<(ostream&, const Expr&);

    Expr_node* p;

public:

    Expr(int);

    Expr(const String&, Expr);

    Expr(const String&, Expr, Expr);

    Expr(const String&, Expr, Expr, Expr);       

    Expr(const Expr& t) { p = t.p; ++p->use; }//由于构造函数为Expr_node分配了内存,需要实现复制构造函数和赋值操作符管理下层的Expr_node

    ~Expr() { if (--p->use == 0) delete p; }

    Expr& operator=(const Expr& t);

    int eval() const { return p->eval(); }//新添加的,为了求表达式的值

};

Expr::Expr(int n)

{

        p = new Int_node(n);

}

 

Expr::Expr(const String& op, Expr t)

{

        p = new Unary_node(op, t);

}

 

Expr::Expr(const String& op, Expr left, Expr right)

{

        p = new Binary_node(op, left, right); //析构函数负责释放

}

 

Expr::Expr (const String& op, Expr left, Expr middle, Expr right)

{

    p = new Ternary_node(op, left, middle, right);

}


当Expr类复制一个Expr_node时,该Expr将其引用计数加1,当引用者为0,删除底层的Expr_node。

Expr&

Expr::operator=(const Expr& rhs) 

{

    rhs.p->use++;

    if (--p->use == 0) 

       delete p;

    p = rhs.p;

    return *this;

}

 

ostream&

operator<<(ostream& o, const Expr& t)

{

        t.p->print(o);

        return o;

}


 

避免复制:让每一个Expr_node包含一个引用计数器,指明同时有多少个Expr指向同一个Expr_node,Expr和Expr_node类协同管理引用计数,当且仅当一个Expr_node的引用计数等于0,该节点才被删除。所以需要在Expr_node中加入引用计数,Expr类声明为友元,帮助管理计数。

class Expr_node {

    friend class Expr;

        friend ostream& operator<<(ostream&, const Expr&);

    int use;

protected:

    Expr_node(): use(1) { }

        virtual void print(ostream&) const = 0;

        virtual int eval() const = 0;

        virtual ~Expr_node() { }

};


 

更改每个派生自Expr_node的类,令其操作为私有,将Expr声明为友元,存储Expr。

class Binary_node: public Expr_node {

    friend class Expr;

       string op;

        Expr left;

        Expr right;

        Binary_node(const string& a, Expr b, Expr c):

                op(a), left(b), right(c) { }

        void print(ostream& o) const

                { o << "(" << left << op << right << ")"; }

};


 

现在用户可以自由地声明Expr的对象和临时对象,构造任意复杂的表达式,而不需要考虑内存管理。

如:Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));

 

求表达式的值

cout << t << " = " << t.eval() << endl;

或者

t = Expr("*", t, t);

    cout << t << " = " << t.eval() << endl;

需要在Expr类中添加函数,把求值传递给其指向的Expr_node。

int eval() const { return p->eval(); }

在Expr_node中 添加一个虚函数。

class Expr_node {

protected:

        virtual int eval() const = 0;

       //其他和前面的相同

};

接着为每个派生自Expr_node的子类添加一个函数来实现求值运算。

Int_node最简单,直接返回其值即可。

int eval() const { return n; }  

对于Unbry_node

int

Unary_node::eval() const

{

    if (op == "-")

                return -opnd.eval();//会调用Expr类的成员eval,然后再调用其指向的Expr_node的虚函数eval,调用到了实际类型的eval();

        throw "error, bad op " + op + " in UnaryNode";

}


 

对Binary_node:

int

Binary_node::eval() const

{

        int op1 = left.eval();

        int op2 = right.eval();

 

        if (op == "-")  return op1 - op2;

        if (op == "+")  return op1 + op2;

        if (op == "*")  return op1 * op2;

        if (op == "/" && op2 != 0)  return op1 / op2;

 

        throw "error, bad op " + op + " in BinaryNode";

}


 

现在可以对表达式进行求值。

 

int main()

{

    Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));

    cout << t << " = " << t.eval() << endl;

    cout << "((-5)*(3+4)) = -35" << endl;

 

    t = Expr("*", t, t);

    cout << t << " = " << t.eval() << endl;

    cout << "(((-5)*(3+4))*((-5)*(3+4))) = 1225" << endl;

    return 0;

}

算术表达式转化为二叉运算树 算术表达式转化为二叉运算树 二叉运算树的实质是不仅记录了表达式而且记录了表达式的运算顺序,运算时直接中序遍历就行了 核心思路:二元表达式由三部分构成: 数 + 运算符 + 数,(数也可以是表达式)在一个表达式中是否能计算其中的单个二元表达式,只取决于此运算符与其前后运算符的优先级,如:3+25,不能计算3+2,因为优先级高于+,应该先计算2*5,在计算+号 #include <iostrea... 阅读详情

相关推荐

算术表达式转二叉树并还原,当有三部分再用斜排方式还原树。

(a+b×(c-d))-e/f :转为树; 前缀:-+a*b-cd/ef 中缀:a+b*c-d-e/f 后缀:abcd-*+ef/- ((a+b)+c*(d+e)+f)*(g+h) "(a+b)+c*(d+e)+f" 这一部分是分成3分:(a+b) + c*(d+e) + f;这样的用+ +斜排方式: 中缀表达式8-(3+5)×(5—6/2...

ZhangJiqun&191cm&85kg 840

【数据结构】超详细讲解:算术表达式转化为后缀表达式、前缀表达式、表达式树的构建

中缀表达式:中缀表达式是我们常见的数学表达式,其中运算符放置在两个操作数中间,例如:3 + 4 * 2。在中缀表达式中,还可以包含括号,用于改变运算符的优先级。后缀表达式:后缀表达式(也称为逆波兰表达式)是一种将运算符放置在操作数后面的表达式,例如:3 4 2 * 1 5 - 2 ^ / +。在后缀表达式中,所有操作符的优先级相同,而括号则不再需要。前缀表达式:前缀表达式(也称为波兰表达式)是一种将运算符放置在操作数前面的表达式,例如:+ / * 3 4 2 ^ - 1 5 2。

Yaoyao2024的博客 2万+

Emuelec系统安装全流程:超详细版指南(从零开始)

手把手带你完成emuelec系统的完整安装,从基础准备到系统配置一步不落,适合新手入门。深入解析emuelec在各类设备上的部署技巧,让复古游戏体验更流畅。

weixin_42594419的博客 1969

C/C++简单思路-简单算术表达式二叉树的构建和求值《数据结构》

实验:编写一个二叉树来表示一个简单算术表达式,树的每一个结点包括一个运算符或运算数。在简单算术表达式中只包含 + - * / 和 一位正整数。并且按要求先乘除后加减的原则构造二叉树。 所示为1+2*3-4/5 代数表达式对应的二叉树,然后由对应二叉树计算该表达式的值 解题思路: 1.给出一个简单算术表达式,形如:1+2*3-4/5,这个表达式其实就是中缀表达式(栈的那一章),也是这颗二叉树的中序遍历; 2.且 二叉树可以由 中序遍历 和 先序 或 后序遍历 求出一颗唯一二叉树,中序表达式已经有

u012164906的博客 4731

SECS/GSM 测试工具 SecsExpress 的配置与实战应用

本文详细介绍了半导体设备通讯测试工具SecsExpress的配置与实战应用。作为专业的SECS/GSM协议调试工具,它能模拟Host与设备端进行双向测试,帮助工程师快速部署、验证通讯链路,并高效排查与RMS系统或真实设备间的联调问题,是提升半导体工厂设备集成与维护效率的利器。

yhn456789的博客 298

计量经济学——事件研究法/事件分析法

计量经济学——事件研究法/事件分析法

weixin_46189461的博客 6752

数据结构基础17:二叉树应用之算术表达式求值

前言:二叉树的一种应用是无歧义地表示代数、关系或逻辑表达式。在上个世纪20年代初期,波兰的逻辑学家发明了一种命题逻辑的特殊表示方法,允许从公式中删除所有括号,称之为波兰表示法。但是,与原来带括号的公式相比,使用波兰表示法降低了公式的可读性,没有得到广泛的使用。在计算机出现后,这一表示法就很有用了,特别是用于编写编译器和解释器。用户输入的待求表达式,也就是中缀表达式,对于人来说,这个很好理解,但是对...

何哥的博客 1万+

AI医疗影像诊断工具2025 完整教程

本教程所述工具均为辅助诊断设备,不可替代执业医师判断。部署使用需遵守当地医疗器械管理法规。推荐开源框架组合:代码太多打包上传网盘了 自行下载。2025年的AI医疗影像诊断工具将以。

m0_60084632的博客 2028

树:表达式树与前中后缀表达式

计算机科学中,除了栈以外,二叉树也是处理表达式的常用工具,为了处理表达式而遵循相应规则构造的树被称为表达式树 表达式树: 算数表达式是分层的递归结构,一个运算符作用于相应的运算对象,其运算对象又可以是任意复杂的表达式。树的递归结构正好用来表示这种表达式。下面只讨论二元表达式。二元表达式可以很自然的联系到二叉树:以基本运算对象作为叶节点中的数据;以运算符作为非叶节点中的数据,其两棵子树是它的运算对象,子树可以是基本运算对象,也可以是复杂表达式。如图是一个表达式树。 前缀...

qfturauyls的博客 2723

2023最新多文明策略手游《万国觉醒内购版》最新整理Linux商业手工服务端+安卓+详细搭建教程

2023最新多文明策略手游《万国觉醒内购版》最新整理Linux商业手工服务端+安卓+详细搭建教程。源码内置详细搭建教程,感兴趣可自行下载。

百创科技 1713

泛微OA集成阿里云短信(E9保姆教程)

泛微OA集成阿里云短信(E9保姆教程)

技术分享 515

表示算术表达式的树

今天看了一遍《C++沉思录》的第八章,作者使用c++实现了表示算术表达式的树。 就是像 (-3)*(3+3)的树就是: [img]http://dl.iteye.com/upload/attachment/407567/7ebf4acc-79bd-3cce-9068-34b6300c381d.gif[/img] 然后,我就用python实现了一下。因为python与C++不同...

mirguest的专栏 455

Opencv计算机视觉PPT-算法篇

01 02 03 04 05 06 07 08 09 010 011 012 013 014 015 016 017 018

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 203

C++面向对象实现算术表达式树

表达式(-5)*(3+4)对应的树如图,包括常数、一元运算符和二元运算符的结点,结点为图中的圆圈,边用箭头表示,一个结点或连接一个子结点、两个子结点或不连接子结点。结点有三种形式:表示整数表达式,包含一个整数值,无子结点,另外两个分别表示一元表达式和二元表达式包含一个操作符,分别有一个或两个子结点,三种形式对应三个类。三个类抽象出来一个结点概念,为它们的基类,即结点类,有子结点的类用包装基类指针的句柄表示“边”,句柄类代码(包装基类指针,动态绑定,隐藏继承层次,引用计数避免复制),其中句柄的使用是核心,

金色的省略号 378

微信小程序 - 解决报错 {“errcode“:41002,“errmsg“:“appid missing, rid: X“} 微信报错错误码errcode“:41002问题分析及解决教程(详细排查)

【微信小程序】报错{“errcode“:41002,“errmsg“:“appid missing, rid: 64e55770-67c1fb7e-027c5a70“}:【微信小程序】微信小程序登录请求出现appid missing提示,微信公众号{"errcode":41002,"errmsg":"appid missing"},微信 报错{“errcode“:41002,“errmsg“:“appid missing,微信小程序获取openid返回appid missing的问题,微信小程序提示Erro

🏆 前端领域优质创作者 5650

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

小白说编译原理-7-算术表达式编译树(支撑类)

简介本章讲述的是编译树的实现,它包含树节点,树的构建,树的遍历三个部分。利用编译树,我们可以构建基本的运算节点以及数字节点,然后遍历树的过程就是执行算术运算的过程。 例如如下的一棵树 叶子节点和分支节点都是一个个的Node,它具有不同的类型(运算符和数字)。代码如下#include <iostream> #include <malloc.h> using namespace std;#def

字节跳动 内推找我 4102

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

利用树求解算术表达式的值

利用树求解算术表达式的值 #include <stdio.h> #include <string.h> #include <malloc.h> //#include "btree.h" #define MaxSize 100 #include<stdlib.h> typedef struct Node { char data; struct Node *lchild; struct Node *rchild; }BTNode,*...

计算机编程开发学习交流的专栏 597

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

算术表达式的转换——算术表达树

Think: 1知识感悟:算术表达式的转换可以思考通过后缀表达式建树,然后分别进行前序遍历,中序遍历,后序遍历即可得到前缀表达式,中缀表达式,后缀表达式SDUT题目链接 建议参考博客算术表达式的转换 Time Limit: 1000MS Memory Limit: 65536KBProblem Description 小明在学习了数据结构之后,突然想起了以前没有解决的算术表达式转化成后缀式的

BlessingXRY的博客 1790

[表达式树] SDUT2484 算术表达式的转换(二叉树遍历)

题目 输入一算术表达式,输出该表达式转换所得到的前缀式 中缀式 后缀式。 Sample Input a*b+(c-d/e)*f# Sample Output +*ab*-c/def a*b+c-d/e*f ab*cde/-f*+ 思路 本题用表达式树,先建一个表达式树,然后这个树的前序,中序,后序遍历分别对应前缀,中缀,后缀表达式。 代码 代码有建立表达式树的方法,以及...

OI - IceCab 757

SDUT2132算术表达式转换成后缀式(表达式树)

题目链接。 解题报告: 这是很久前做的了。。又扒出来了。。怕再忘了嘛。。 很简单。建立表达式树,然后后序遍历。 代码如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define ...

weixin_30362233的博客 95

java 表达式树,在Java中解析算术表达式并从中构建树

I needed some help with creating custom trees given an arithmetic expression. Say, for example, you input this arithmetic expression:(5+2)*7The result tree should look like:*/ \+ 7/ \5 2I have som...

weixin_30531997的博客 265

数据结构—— 基于二叉树的算术表达式求值

标题实验五 基于二叉树的算术表达式求值 【实验目的】 1.掌握二叉树的二叉链表存储表示和二叉树的遍历等基本算法。 2.掌握根据中缀表达式创建表达式树的算法 3.掌握基于表达式树的表达式求值算法。 【实验内容】 问题描述 输入一个表达式(表达式中的数均为小于10的正整数),利用二叉树来表示该表达式,创建表达式树,然后利用二叉树的遍历操作求表达式的值。 输入要求 多组数据,每组数据一行,对应一个算术表达式,每个表达式均以“=”结尾。当表达式只有一个“=”时,输入结束。 输出要求 每组数据输出1行,为表达式的

qq_50675813的博客 1万+

算术表达式的转换

题目描述 小明在学习了数据结构之后,突然想起了以前没有解决的算术表达式转化成后缀式的问题,今天他想解决一下。 因为有了数据结构的基础小明很快就解出了这个问题,但是他突然想到怎么求出算术表达式的前缀式和中缀式呢?小明很困惑。聪明的你帮他解决吧。 输入 输入一算术表达式,以\'#\'字符作为结束标志。(数据保证无空格,只有一组输入) 输出 输出该表达式转换所得到...

anyi7265的博客 293

“C++学习笔记” 关于二叉树:二叉表示树判断算术表达式是否存在错误

输入一个算术表达式,判断该表达式是否合法,若不合法,给出错误信息;若合法,则输出合法表达式的表达式树。 【数据模型】二叉表示树具有以下特点:1.叶子结点一定是操作数 2. 分支结点一定是操作符 【算法分析】表达式不合法有三种情况:①左右括号不匹配;②变量名不合法;③运算符两旁无参与运算的变量或数。 【解题思路】1. 根据运算符的优先顺序,将表达式结合成(左操作符 运算符 右操作符)的形式 2. 由外层括号开始,运算符作为二叉表示树的根结点,左操作数作为左子树,右操作数作为右子树 3. 如果某子..

Kowlooncen的博客 419

python实现基本的二叉树结构以及生成表达式树

该篇代码的实现用到了上一篇中定义的栈,和前一篇的目的一样。如果有错误的地方或更好的办法,请不吝赐教,感谢感谢! 树节点的定义: class tree_Node(object): # 树节点类 def __init__(self,elem): self.elem = elem self.left = None self.right = N...

qq_39816087的博客 2835
上一篇: MLP、RBF、SVM网络比较及其应用前景
下一篇: 如何设计容器类
xiaoding133
博客等级 码龄19年 415粉丝 159原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值