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

题目

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

分析

如上图所示的二叉树,就表示表达式1+2*3-4/5,如果我们需要计算他,就需要采用前序遍历的方法。
在这里插入图片描述

建树

如下图所示是随便构造的一颗二叉树
在这里插入图片描述
他表示的是表达式:5 + 2 - 1 * 2 / 5 + 3 + 2* 3
从图中可以发现:

  1. 所有的叶子节点都必须是数字。
  2. 所有的非叶子节点都是符号,而且都必须有左子树和右子树。
  3. 不可能会存在一个节点仅存在左子树而不存在右子树,或者仅存在右子树而不存在左子树的情况。
  4. + - 节点只存在于最左边,左孩子只能是+ - 或者数字,右孩子只能是* /或者数字
  5. * / 节点的右孩子只能是数字,左孩子只能是* /或者数字

第1 2 3条非常好理解,主要是第4条和第5条

第4条:因为我们计算的时候是采用前序遍历,前序遍历的顺序是自身、左子树、右子树,那么递归返回的顺序是右子树、左子树,乘法和除法的优先级高,所以必须要先算完乘除再算加减,所以加减号要放在左边

第5条:举个反例:
在这里插入图片描述
这个树表示1 / (2 * 5),而我们的表达式是不带括号的。

建树方法:从右往左扫描字符串,优先寻找+ -,如果不存在+ - 则寻找* /,如果都不存在,说明这是一个叶子节点。一旦寻找到了符号,都会利用该符号之前的子串来递归构建左子树,右边的子串会用来递归构建右子树。

代码

#include <bits/stdc++.h>
using namespace std;

struct TreeNode{
	int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int v){
        this->val = v;
        this->left = NULL;
        this->right = NULL;
    }
    TreeNode(char v,TreeNode* l,TreeNode *r){
        this->val = v;
        this->left = l;
        this->right = r;
    }
};

/*
string字符串转换成Int类型
*/
int stringToInt(string s){
	reverse(s.begin(),s.end());
	int res = 0;
	for(int i = 0;i<s.size();++i){
		res += (s[i]-'0') * pow(10,i);
	}
	return res;
}


/*
构造二叉树
*/
TreeNode* init(string s){
	if(s == ""){
		return NULL;
	}
	//首先找到+或者-
	int i = s.size() - 1;
	while(i>=0 && s[i] != '+' && s[i] != '-'){
		--i;
	}
	if(i >= 0){
		TreeNode* t = new TreeNode(s[i]);
		t->left = init(s.substr(0,i));
		t->right = init(s.substr(i+1));
		return t;
	}
	//找不到+-就找乘除
	i = s.size()-1;
	while(i>=0 && s[i] != '*' && s[i] != '/'){
		--i;
	}
	if(i >= 0){
		TreeNode* t = new TreeNode(s[i]);
		t->left = init(s.substr(0,i));
		t->right = init(s.substr(i+1));
		return t;
	}
	//找不到,则说明这是一个数值
	TreeNode* t = new TreeNode(stringToInt(s));
	return t;
}

/*
计算
*/
double calcular(TreeNode* t){
	if(!t){
		return 0;
	}
	double res = 0;
	if(t->left && t->right){
		//有左子树和右子树,说明当前表示一个符号
		switch (t->val){
		case '+':
			res = calcular(t->left) + calcular(t->right);
			break;
		case '-':
			res = calcular(t->left) - calcular(t->right);
			break;
		case '*':
			res = calcular(t->left) * calcular(t->right);
			break;
		case '/':
			res = calcular(t->left) * 1.0 / calcular(t->right);
			break;
		}
	}else{
		//没有左右子树(不可能会出现左子树不为空而右子树为空的情况)
		res = t->val;
	}
	return res;
}

/*
递归销毁二叉树
*/
void destory(TreeNode* node){
    if(node == NULL){
        return;  //已经为NULL,无需销毁
    }
    destory(node->left);
    destory(node->right);
    node->left = NULL; //销毁之后需要把指针置空
    node->right = NULL;
    delete node;
}

int main() {
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	TreeNode* t = init("1+2*3-4/5");
	cout << "1+2*3-4/5 = " << calcular(t) << endl;
	TreeNode* t2 = init("1+2*3+36+10*3+25/6");
	cout << "1+2*3+36+10*3+25/6 = " << calcular(t2) << endl;
	
	destory(t);
	t = NULL;
	return 0;
}

如果存在什么不对的地方,欢迎指正!

  • 10
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
算术表达式可以通过二叉树来表示,每个节点表示一个操作符或操作数。对于操作符节点,左子节点表示操作符左侧的表达式,右子节点表示操作符右侧的表达式;对于操作数节点,左右子节点都为空。 创建二叉树的过程可以通过递归实现,具体步骤如下: 1. 将表达式字符串转换为字符数组,依次遍历每个字符; 2. 如果当前字符是数字,则构造一个操作数节点,并将该节点作为当前节点的左子节点; 3. 如果当前字符是操作符,则构造一个操作符节点,并将该节点作为当前节点的右子节点; 4. 对于操作符节点,递归调用创建二叉树的函数,将左侧表达式字符串作为参数传入左子节点,将右侧表达式字符串作为参数传入右子节点; 5. 返回根节点。 对于求值操作,也可以通过递归实现,具体步骤如下: 1. 如果当前节点是操作数节点,则直接返回该节点的值; 2. 如果当前节点是操作符节点,则递归调用求值函数,将左子节点的值作为参数传入左侧表达式,将右子节点的值作为参数传入右侧表达式,然后根据操作符计算结果并返回。 在 C 语言中,可以使用结构体来定义二叉树节点,如下所示: ``` typedef struct TreeNode { char data; struct TreeNode *left; struct TreeNode *right; } TreeNode; ``` 使用该结构体,可以方便地创建二叉树和进行求值操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值