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

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


题目要求

先用二叉树来表示一个简单算术表达式,树的每一个结点包括一个运算符或运算数。在简单算术表达式中只包含 加 减 乘 除 和一位正整数且格式正确(不包括括号),并且要按照先乘除后加减的原则构造二叉树,下图所示为 “1+2*3-4/5” 代数表达式对应的二叉树,然后由对应的二叉树计算该表达式的值。

先用二叉树来表示一个简单算术表达式,树的每一个结点包括一个运算符或运算数。
在简单算术表达式中只包含 + - * / 和一位正整数且格式正确(不包括括号),
并且要按照先乘除后加减的原则构造二叉树,下图所示为 “1+2*3-4/5” 代数表达式对应的二叉树,
然后由对应的二叉树计算该表达式的值。

img001

思考过程
	按照题目所述, 自己根据一个更复杂更全面的表达式(出现了所有的情况),画多一个图,寻找规律。
	
	表达式的要求:
		(1)要出现 + - * / 中所有的符号
		(2)各优先级符号要出现不同的次数来相互搭配
		
	如:1+2+3*4*5+6+7+8/2*9+9

我大意了,在作图的时候忘记用减号 - 了,但在这里没有影响

img2

二叉树的特点
1. 数字只能是叶子节点, 且叶子节点只能是数字
2. + - 符号永远只能祖宗节点或者左孩子节点
3. 如果遇到 * / 则 将其作为祖宗节点来看, 其右孩子节点只能是 数字, 左孩子节点是 * / 符号或者 数字
思路分析
1. 遍历字符串,利用栈存储节点:
        p = new BTNode;
        p->lchild = NULL;
        p->rchild = NULL;
	
	(1) 遇到数字:
		思路:
		若分情况讨论情况:
			1. 栈为空,即这是字符串第一个数字
			2. 左右边可能为 + - * / 其中之一,
			3. 而如果是乘除,则要先进行乘除运算
			可见,如果对数字进行分情况处理,将会十分复杂,
				不妨考虑直接将其压栈,
				在遇到运算符在进行符号的优先级进行讨论。
		
		代码操作:p->elem = ch;  sta.push(p);
			
    (2) 遇到乘除:
        思路:将 * (或/) 作为父节点,
        	左节点为栈顶元素 num1 或者 * (或/),
        	右节点为字符串下一个元素num2
        
        代码操作:新建节点 p 赋值 * (或/),
        		新建节点 r 赋值 字符串下一个元素num2,
        		弹出栈顶元素作为节点l, 
        		然后连接:p->lchild = l, p->rchild = r;
			
			p->elem = ch;
            BTNode *r = new BTNode;
            i++;
            r->elem = str[i];
            r->lchild = NULL;
            r->rchild = NULL;
            p->rchild = r;
            p->lchild = sta.top();
            sta.pop();
            sta.push(p);
			
	(3) 遇到加减: 
        思路:通过观察二叉树中,父节点的左右孩子节点的各种可能找出规律
        	两种情况: [1]栈内元素个数为2,
        			则栈顶元素必为 * 或 / ,
        			则将其作为当前节点右的孩子节点,
        			然后弾栈,剩下元素处理过程与 [2] 一致
        			 [2]栈内元素个数为1,
        			 则栈内元素必为 +、-、数字(首数字)其中之一,
        			 则将其作为当前节点的左孩子节点即可,
        			 然后弾栈,将当前节点压栈
        			 
        代码操作:
           p->elem = ch;
            if(sta.size() == 2) {
                BTNode *r = sta.top();
                sta.pop();
                sta.top()->rchild = r;
            }
            p->lchild = sta.top();
            sta.pop();
            sta.push(p);
            
2. 遍历字符串完成后,观察到有几种情况
	(1)只有 + (或-)操作, 则栈内有两个元素,
		栈底元素是数字,栈顶元素是运算符。
	(2)只有 * 或(/)操作,则栈内只有一个运算符。
	(3)既有 + (或-)操作也有 * 或(/)操作,则栈内有两个元素,
		栈底元素是 * (或/)或数字,栈顶元素是运算符。
	
	所以可以看成(2)是一种情况,(1)(3)是另一种情况
	
	代码操作:
		if (sta.size() == 2) {
            BTNode *r = sta.top();
            sta.pop();
            sta.top()->rchild = r;
        }
        BT = sta.top();
代码实现
#include <iostream>
#include <string>
#include <cstring>
#include <stack>
using namespace std;

#define ElemType char

typedef struct node {
    ElemType elem;
    struct node *lchild;
    struct node *rchild;
}BTNode;

void createBT(BTNode* &BT, string str) {
    stack<BTNode*> sta;
    BTNode *p;
    for(int i = 0; i < str.size(); i++) {
        p = new BTNode;
        p->lchild = NULL;
        p->rchild = NULL;
        char ch = str[i];
        if(ch == '*' || ch == '/') {
            p->elem = ch;
            BTNode *r = new BTNode;
            i++;
            r->elem = str[i];
            r->lchild = NULL;
            r->rchild = NULL;
            p->rchild = r;
            p->lchild = sta.top();
            sta.pop();
            sta.push(p);
        } else if(ch == '+' || ch == '-') {
            p->elem = ch;
            if(sta.size() == 2) {
                BTNode *r = sta.top();
                sta.pop();
                sta.top()->rchild = r;
            }
            p->lchild = sta.top();
            sta.pop();
            sta.push(p);
        } else {
            p->elem = ch;
            sta.push(p);
        }
        p = NULL;
        free(p);
    }

    if (sta.size() == 2) {
        BTNode *r = sta.top();
        sta.pop();
        sta.top()->rchild = r;
    }
    BT = sta.top();
}

int calculate(BTNode* &BT) {
    char ch = BT->elem;
    if(ch == '+') return calculate(BT->lchild) + calculate(BT->rchild);
    else if(ch == '-') return calculate(BT->lchild) - calculate(BT->rchild);
    else if(ch == '*') return calculate(BT->lchild) * calculate(BT->rchild);
    else if(ch == '/') return calculate(BT->lchild) / calculate(BT->rchild);
    else return ch - '0';
}

void displayBT(BTNode* &BT) {
    if(BT != NULL){
		cout << BT->elem;
		displayBT(BT->lchild);
		displayBT(BT->rchild);
	}
	else{
		printf("#");
	}
}

void destroyBT(BTNode* &root) {
    if(root != NULL) {
        destroyBT(root->lchild);
        destroyBT(root->rchild);
        free(root);
    }
}

int main() {
    string str = "1+2+3*4*5+6+7+8/2*9+9";
    BTNode *BT;

    createBT(BT, str);
    cout << "简单算术表达式二叉树为:\n";
    displayBT(BT);

    cout << "\n\n该表达式的运算结果为:" << calculate(BT) << "\n\n";

    destroyBT(BT);

    system("pause");
    return 0;
}
运算结果

img003

总结
	知识点:根据算术运算符的优先级进行分类讨论,运用了栈这一数据结构。
	
	拓展思考:上述代码先把 * (或/) 的运算进行操作,
			而 + (或-) 则先存在栈里,不难发现,
			可以不通过构建二叉树,利用栈来运算简单的表达式。
	
	拓展知识:
		题目所给这一类表达式叫中缀表达式,
		 指操作符是以中缀形式处于操作数的中间,
		 这一类表达式是人们常用的算术表示方法,
		 但通过这道题可以发现,中缀表达式不容易被计算机解析,
		 要使用还要经过处理(利用栈)。
		而前缀表达式(波兰式,波兰数学家Jan Lukasiewicz)
		 与后缀表达式(逆波兰式, RPN),则更容易被计算机进行识别运算。
  • 40
    点赞
  • 174
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
算术表达式可以通过二叉树表示,每个节点表示一个操作符或操作数。对于操作符节点,左子节点表示操作符左侧的表达式,右子节点表示操作符右侧的表达式;对于操作数节点,左右子节点都为空。 创建二叉树的过程可以通过递归实现,具体步骤如下: 1. 将表达式字符串转换为字符数组,依次遍历每个字符; 2. 如果当前字符是数字,则构造一个操作数节点,并将该节点作为当前节点的左子节点; 3. 如果当前字符是操作符,则构造一个操作符节点,并将该节点作为当前节点的右子节点; 4. 对于操作符节点,递归调用创建二叉树的函数,将左侧表达式字符串作为参数传入左子节点,将右侧表达式字符串作为参数传入右子节点; 5. 返回根节点。 对于求值操作,也可以通过递归实现,具体步骤如下: 1. 如果当前节点是操作数节点,则直接返回该节点的值; 2. 如果当前节点是操作符节点,则递归调用求值函数,将左子节点的值作为参数传入左侧表达式,将右子节点的值作为参数传入右侧表达式,然后根据操作符计算结果并返回。 在 C 语言中,可以使用结构体来定义二叉树节点,如下所示: ``` typedef struct TreeNode { char data; struct TreeNode *left; struct TreeNode *right; } TreeNode; ``` 使用该结构体,可以方便地创建二叉树和进行求值操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Quantum_Wu

一起加油呀ヾ(◍°∇°◍)ノ゙

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值