表达式二叉树(简要)

表达式二叉树

一、题目表述

使用二叉树来求解表达式输入一个表达式(表达式中的数可以是多位数,可以含多层括号),利用二叉树来表示该表达式,创建表达式树,然后利用二叉树的遍历操作求表达式的值。

二、分析

2.1 解决思路

  • 1、 输入为中缀表达式,直接使用中缀表达式建立二叉树较为麻烦,可将中缀表达式转换为前后缀表达式再进行建树。本次实验使用后缀表达式建树。
  • 2、将中缀表达式转为后缀表达式可使用栈。
  • 3、要考虑多位数、浮点数,本次实验采用每个操作符都为字符串,即将原表达式分为若干子字符串,以分离多位数、浮点数和运算符。
  • 4、利用栈建立二叉树,并用递归求值。

2.2 中缀表达式转为后缀表达式

  • 1、中缀表达式即我们常见数学表达式入1+3*(5-4)/6,转为后缀表达式更便于计算机识别计算。
  • 2、转换过程使用一个栈,一个用于存储结果的后缀表达式数组
  • 一般过程为:
    • 1)从左到右扫描每个字符
    • 2)针对每个字符有以下操作:
      • i)如果是(直接入栈(操作符栈)
      • ii)如果为),弹栈,直到遇到’('为止,将元素放入后缀数组
      • iii)+-*/循环判断若当前元素优先级低则弹栈将其放入后缀表达数组,循环结束意味着当前元素优先级高,则入栈
      • iiii)数字直接入后缀数组
      • iiiii)扫描完中缀表达式后,将操作符栈中的所有操作符弹出并输出到后缀表达式栈

2.3 建树

  • 一个用于建树的栈
  • 遇到数字直接入栈,遇到运算符则弹出两个元素作为运算符的两个子结点,然后压栈
  • 最后栈中只剩一个元素,即为最后的表达式树

2.4 遍历求值

  • 递归求值(中序)

三、具体实现

3.1 将字符串分离

  • 由于有多位数、浮点数等,单个字符操作复杂,所以将表达式转为若干子字符串
	/**
	 * @brief 将数字和运算符分割成子字符串存入vector向量中
	 * @param expreesion 原表达式
	 * @param result 分割后的子字符串向量
	*/
	void ExpressionSplit(string expreesion, vector<string>& result) {
		int i = 0;
		while (expreesion[i] != '\0') {			//循环读取表达式内容
			switch (expreesion[i]) {
			case '(':
			case ')':
			case '+':
			case '-':
			case '*':
			case '/': {							//若为运算符
				string bufstrings;				//临时字符串
				bufstrings += expreesion[i];	//赋给临时字符串
				result.push_back(bufstrings);	//放入字符串向量容器中
				i++;
				break;
			}
			default: {
				string bufstring;
				while ((expreesion[i] >= '0' && expreesion[i] <= '9')
					|| expreesion[i] == '.') {	//若为数字或小数点,循环读取到不是数字或小数点为止
					bufstring += expreesion[i];	//用临时字符串接收
					i++;
				}
				result.push_back(bufstring);	//放入容器
				break;
			}
			}
		}
	}

3.2 中缀转后缀

  • 将分离好的中缀表达式字符串容器进行后缀转换
	/**
	 * @brief 将中缀表达式转为后缀表达式(表达式为已经进行数字运算符分离后的容器)
	 * @param infix 中缀表达式
	 * @param suffix 后缀表达式
	*/
	void ExpressionConversion::InfixToSuffix(vector<string> infix, vector<string>& suffix) {
		for (auto& i_infix : infix) {		//遍历中缀表达式
			switch (i_infix[0]) {			//字符串首元素即可判断是数字还是运算符
			case '(': {						//如果是'('直接入栈(操作符栈)
				Push("(");
				break;
			}
			case ')': {						//如果为')',弹栈,知道遇到'('为止,将元素放入后缀表达式容器
				string e = Pop();
				while (e[0] != '(') {
					suffix.push_back(e);
					e = Pop();
				}
				break;
			}
			case '+':
			case '-':
			case '*':
			case '/': {
				if (IsEmpty()) {			//栈为空则直接入栈
					Push(i_infix);
					break;
				}
				string e = GetTop();
				while (GetPriority(i_infix[0]) <= GetPriority(e[0])) {	//否则循环判断若当前元素优先级低则弹栈将其放入后缀表达式容器
					suffix.push_back(Pop());
					if (IsEmpty())
						break;
					e = GetTop();			
				}
				Push(i_infix);				//循环结束意味着当前元素优先级高,则入栈
				break;
			}
			default: {						//数字直接入后缀数组
				if (i_infix[0] >= '0' && i_infix[0] <= '9') {
					suffix.push_back(i_infix);
					break;
				}
				break;
			}
			}
		}
		while (!IsEmpty()) {				//扫描完中缀表达式后,将操作符栈中的所有操作符弹出并输出到后缀表达式栈
			suffix.push_back(Pop());
		}
	}
  • 在此之中判断了运算符的优先级,具体函数为
	/**
	 * @brief 返回字符的优先级
	 * @param ch 需要获取优先级的字符
	 * @return 
	*/
	int ExpressionConversion::GetPriority(char ch) {
		switch (ch) {
		case '(':
			return 0;
		case '+':
		case '-':
			return 1;
		case '*':
		case '/':
			return 2;
		default:
			return -1;
		}
	}

3.3 建树

  • 使用栈建立二叉树
	/**
	 * @brief 解析表达式,建立表达式树
	 * @param expression 原表示
	 * @return 表达式树的根节点
	*/
	TreeNode* MathExpressionTree::ParseExpression(string expression) {
		vector<string> inffix;					//分割后子字符串容器
		ExpressionSplit(expression, inffix);	//将原表达式转换为数字运算符分离的子字符串
	
		ExpressionConversion ec;				//用于将中缀表达式转为后缀表达式
		vector<string> suffix;					//后缀表达式子字符串容器
		ec.InfixToSuffix(inffix, suffix);		//转换函数
	
		Stack<TreeNode* > treestack;			//用于进行表达式转为树的栈
		for (auto& i_suffix : suffix) {			//遍历容器
			switch (i_suffix[0]) {				
			case '+':
			case '-':
			case '*':
			case '/': {							//判断首元素即可判断是数字还是运算符
				TreeNode* bufnode = new TreeNode(i_suffix);		//是运算符新建节点
				bufnode->rchild = treestack.Pop();
				bufnode->lchild = treestack.Pop();
				treestack.Push(bufnode);						//操作完后入栈
				break;
			}
			default: {
				if (i_suffix[0] >= '0' && i_suffix[0] <= '9') {	//若是数字
					treestack.Push(new TreeNode(i_suffix));		//直接入栈
					break;
				}
				break;
			}
			}
		}
		return treestack.Pop();
	}

3.4 遍历求值

  • 即递归求值
	/**
	 * @brief 表达式遍历求值过程
	 * @param node 传入根节点
	 * @return 计算结果
	*/
	double MathExpressionTree::Evaluate(TreeNode* node) {
		if (node == nullptr)
			return 0.0;
		switch (node->data[0]) {
		case '+':
			return Evaluate(node->lchild) + Evaluate(node->rchild);
		case '-':
			return Evaluate(node->lchild) - Evaluate(node->rchild);
		case '*':
			return Evaluate(node->lchild) * Evaluate(node->rchild);
		case '/':
			return Evaluate(node->lchild) / Evaluate(node->rchild);
		default:
			return stod(node->data);	//将字符串类型转为double类型
		}
	}

四、结果

  • 编写测试函数
	//测试函数
	void MathExpressionTreeTest() {
		string str[5];						//定义五个表达式
		str[0] = "1+2*(3.3+4)/20";
		str[1] = "1+2*(5-3)/2";
		str[2] = "12/3+2.5/4-1";
		str[3] = "4.8*2+0.5-3";
		str[4] = "1+2-3*4/5+(6-7)*(8/9)";
		cout << "有以下表达式:" << endl;
		for (int i = 0; i < 5; i++) {
			cout << str[i] << endl;
		}
		cout << endl;
	
		MathExpressionTree met0(str[0]);	//表达式转换到二叉树
		MathExpressionTree met1(str[1]);
		MathExpressionTree met2(str[2]);
		MathExpressionTree met3(str[3]);
		MathExpressionTree met4(str[4]);
		cout << "解析为二叉树后:" << endl;	//转换结果
		cout << str[0] << "\t\t==> " << met0.ToString() << endl;
		cout << str[1] << "\t\t==> " << met1.ToString() << endl;
		cout << str[2] << "\t\t==> " << met2.ToString() << endl;
		cout << str[3] << "\t\t==> " << met3.ToString() << endl;
		cout << str[4] << "\t==> " << met4.ToString() << endl;
		cout << endl;
	
		cout << "计算结果:" << endl;			//最后值
		cout << str[0] << "\t\t= " << met0.GetResult() << endl;
		cout << str[1] << "\t\t= " << met1.GetResult() << endl;
		cout << str[2] << "\t\t= " << met2.GetResult() << endl;
		cout << str[3] << "\t\t= " << met3.GetResult() << endl;
		cout << str[4] << "\t= " << met4.GetResult() << endl;
		
	}
  • 在main函数中调用,运行得到结果
	有以下表达式:
	1+2*(3.3+4)/20
	1+2*(5-3)/2
	12/3+2.5/4-1
	4.8*2+0.5-3
	1+2-3*4/5+(6-7)*(8/9)
	
	解析为二叉树后:
	1+2*(3.3+4)/20          ==> +(1,/(*(2,+(3.3,4)),20))
	1+2*(5-3)/2             ==> +(1,/(*(2,-(5,3)),2))
	12/3+2.5/4-1            ==> -(+(/(12,3),/(2.5,4)),1)
	4.8*2+0.5-3             ==> -(+(*(4.8,2),0.5),3)
	1+2-3*4/5+(6-7)*(8/9)   ==> +(-(+(1,2),/(*(3,4),5)),*(-(6,7),/(8,9)))
	
	计算结果:
	1+2*(3.3+4)/20          = 1.73
	1+2*(5-3)/2             = 3
	12/3+2.5/4-1            = 3.625
	4.8*2+0.5-3             = 7.1
	1+2-3*4/5+(6-7)*(8/9)   = -0.288889
- 经验证,结果正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值