算术表达式转成后缀表达式(逆波兰式)并求值

   前几天博主去参加一家企业的笔试,其中编程题的最后一道就是数学表达式求值的问题,由于数据结构已经学过好长时间了,所以就忘了,回来之后就赶紧翻书查看,才知道在栈和二叉树这两章中都有关于数学表达式求值的问题(由此可见,数据结构和算法这些基础知识对程序员来说有多重要,所以一定要好好学!)。接下来楼主就将自己了解到的知识分享出来,供大家参考,不足之处,还请各位多多指教!

算术表达式转成后缀表达式(逆波兰式)

   我们常见的表达式都是类似于(a+b)*c-d/e*f之类的形式,这种形式都于人来说很容易理解,因为我们很清楚这些运算符的优先级,所以会选择哪些先算哪些后算,但是对于计算机来说,这种方式却很难办到。因此,我们需要将计算的方式设计的越简单越好,越简单的计算形式越便于计算机处理,执行效率也就更高,准确率也更高。因此,计算机方面的大牛们就设计了前缀表达式(波兰表达式)、中缀表达式、后缀表达式(逆波兰式),这三种表达式是根据遍历二叉树的不同顺序得出来的,相信学过数据结构的同学对这几种方式并不陌生。好了,废话不多说,接下来,我们就看一下如何将一个数学表达式转成后缀表达式并利用后缀表达式来计算结果。
   如何将(a+b)*c-d/e*f转成后缀表达式呢?
   我们约定 ‘*’,‘/’两种运算符的优先级最高,用整数3表示,‘+’,‘-’的运算符的优先级次之,用2表示,‘(’的优 先级最低,用1表示;
   具体思想如下:
   1. 建立一个栈用来保存操作符,初始时为空;
   2. 从左至右遍历输入的算术表达式;
      1) 如果当前字符为操作数,则直接加入到后缀表达式中;
      2) 如果当前字符为操作符,当栈为空直接将此操作符加入到栈中,否则就将栈中所有操作符优先级小于当前操作符优先级的操作 符全部加入                     到后缀表达式中(如果栈不为空且栈顶操作符的优先级大于等于当前操作符的优先级就将栈顶元素出栈并加入后缀表达式中,直到栈为空或栈顶           操作符的优先级小于当前操作符的优先级),最后将当前操作符入栈。
     3) 如果当前字符为‘(',直接将其加入操作符栈中;
     4) 如果当前字符为‘)’,就将栈顶操作符出栈,直到遇到第一个‘(’为止。(注意,需要将‘(’从操作数 栈中取出但不需要加入到后缀表达式                中)。
  3. 读取完毕后,将栈中剩余操作符挨个出栈并加入到后缀表达式中。

根据后缀表达式求值

根据后缀表达式求值的思想如下:
   1. 建立操作数栈用来保存操作数;
   2. 从左至右遍历后缀表达式;
      1) 如果当前字符为操作数则入栈;
      2) 如果当前字符为操作符则从栈中弹出两个操作数,并用后弹出的操作数操作先弹出的操作数(读者可以想一想这句话和“用先 弹出的操作数操作           后弹出的操作数”有什么不一样,其实这和栈的FILO有关)。然后再将操作后的结果入栈。
   3. 重复步骤2,当遍历完整后缀表达式,栈中的操作数即为最后的结果。
  下面是对以上表述的java代码实现:
import java.util.HashSet;
import java.util.Scanner;
import java.util.Stack;

public class InToProfix1 {

	private static HashSet<Character> optr = new HashSet<Character>();
	private static void initOptr() {
		optr.add('+');
		optr.add('-');
		optr.add('*');
		optr.add('/');
	}
	
	//判断字符是否为操作符
	private static boolean isOptr(char c) {
		if(optr.contains(c))
			return true;
		return false;
	}
	
	//判断字符是否为操作数
	private static boolean isOperand(char c) {
		if(c>='0'&&c<='9')
			return true;
		return false;
	}
	
	//得到当前操作符的优先级
	private static int priority(char c) {
		switch(c) {
			case'*':
			case'/':
				return 3;
			case'+':
			case'-':
				return 2;
			case'(':
				return 1;
			default:{
				System.out.println("输入错误!");
				return 0;
			}
		}
	}
	
	//用后缀表达式求值
	public static int numberCalculate(String profixExpr) {
		Stack<Integer> count = new Stack<Integer>();
		char c;
		int number1,number2;
		for(int i = 0; i < profixExpr.length(); i++) {
			if(isOperand(c = profixExpr.charAt(i)))
				count.push(c - '0');
			else {
				number2 = count.pop();
				number1 = count.pop();
				switch(c) {
					case'+':
						count.push(number1 + number2);
						break;
					case'-':
						count.push(number1 - number2);
						break;
					case'*':
						count.push(number1 * number2);
						break;
					case'/':
						count.push(number1 / number2);
						break;
				}
			}
		}
		return count.pop();
	}
	
	public static void main(String[] args) {
		
		initOptr();
		Scanner scan = new Scanner(System.in);
		String expression = scan.nextLine();					//输入的表达式
		StringBuilder profixExpr = new StringBuilder();			//保存已将建立的后缀表达式
		Stack<Character> optr = new Stack<Character>();			//操作符栈
		char c;													//输入表达式某个位置的字符
		char pop;												//运算符栈中弹出的字符
		
		for(int i = 0; i < expression.length(); i++) {
			c = expression.charAt(i);
			//如果当前字符为操作数
			if(isOperand(c)) {
				profixExpr.append(c);
			}
			
			//如果当前字符为操作符
			else if(isOptr(c)) {
				if(optr.isEmpty())
					optr.push(c);
				else {
					while(true) {
						if(optr.isEmpty() || priority(optr.peek()) < priority(c))
							break;
						pop = optr.pop();
						profixExpr.append(pop);
					}
					optr.push(c);
				}
			}
			
			//如果当前字符为‘(’
			else if('(' == c) {
				optr.push(c);
			}
			
			//如果当前字符为‘)’
			else if(')' == c) {
				while((pop = optr.pop() ) != '(')
					profixExpr.append(pop);
			}
			
			else
				System.out.println("输入得表达式有错误:=======>"+c);
		}
		while(!optr.isEmpty())
			profixExpr.append(optr.pop());
		System.out.println("转换后的后缀表达式为:"+profixExpr);
		System.out.println("后缀表达式计算的结果为:"+numberCalculate(profixExpr.toString()));
	}
	
}



输入一个表达式,测试结果如下:


其实,数学表达式的求值问题也可以先转为前缀表达式(逆波兰式),然后利用前缀表达式求值,并且各种表达式之间也可以相互转换,有兴趣的读者可以关注博主的其它文章。由于博主水平有限,文章中难免有不足之处,希望各位读者谅解并及时反馈,以便于博主及时更正。
  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 这个实验的目的是将一般算术表达式转换成后缀式。后缀式也叫逆波兰式,是一种不含括号的表达式,运算符在操作数的后面,因此也叫后缀表达式。转换后的后缀式可以方便地进行计算,而且不需要考虑运算符的优先级和括号的问题。 转换算法的基本思路是利用栈来存储运算符,遇到操作数直接输出,遇到运算符则将其与栈顶运算符比较优先级,如果栈顶运算符优先级高,则将栈顶运算符弹出并输出,直到栈顶运算符优先级低于当前运算符或者栈为空,然后将当前运算符入栈。最后将栈中剩余的运算符依次弹出并输出。 例如,将中缀表达式"3+4*5-6"转换成后缀表达式的过程如下: 1. 从左到右扫描中缀表达式,遇到操作数直接输出,即输出3; 2. 遇到运算符"+",将其入栈; 3. 遇到操作数4,直接输出,即输出4; 4. 遇到运算符"*",由于"*"的优先级高于"+",所以将其入栈; 5. 遇到操作数5,直接输出,即输出5; 6. 此时栈顶为"*",优先级高于"+",所以将"*"弹出并输出,即输出4 5 *; 7. 遇到运算符"-",由于"-"的优先级低于"*",所以将"-"入栈; 8. 遇到操作数6,直接输出,即输出6; 9. 此时栈顶为"-",优先级低于"*",所以将6入栈; 10. 扫描结束,将栈中剩余的运算符依次弹出并输出,即输出3 4 5 * + 6 -。 最终得到的后缀表达式为"3 4 5 * + 6 -"。 ### 回答2: 一般算术表达式是指包含加、减、乘、除等运算符及其优先级,以及括号的表达式。在计算机中,我们需要将这种表达式转换成后缀式,以方便计算机的处理。后缀式也叫逆波兰式,是一种把运算符后置的表达式,例如将中缀表达式“9+5*3-8/4”转换为后缀表达式为“9 5 3 * + 8 4 / -”。 算法思想: 我们可以借助栈来实现中缀表达式后缀表达式,具体来说,我们需要先遍历一遍中缀表达式,然后按照优先级将操作符加入栈中,将数字直接输出。遍历完成之后,将栈中所有操作符按照顺序依次输出,这时候得到的序列就是后缀表达式。 具体实现: 1. 遍历中缀表达式,对于数字直接输出。 2. 对于遇到的操作符,如果是左括号,直接入栈;如果是右括号,则将栈中的操作符出栈,直到遇到左括号停止,并将左右括号丢弃;如果是其他操作符,则需要将栈中比当前操作符优先级更高的操作符全部出栈,放到后缀表达式中,然后将当前操作符入栈。 3. 遍历完成后,将栈中操作符全部出栈并添加到后缀表达式中。 4. 最后得到的序列就是后缀表达式。 代码实现: ```python def infixToPostfix(expression): operators = {'+': 1, '-': 1, '*': 2, '/': 2, '(': 0, ')': 0} # 操作符优先级 stack = [] postfix = [] for ch in expression: if ch.isdigit(): # 直接输出数字 postfix.append(ch) else: if ch == '(': stack.append(ch) elif ch == ')': while stack and stack[-1] != '(': postfix.append(stack.pop()) if stack and stack[-1] == '(': stack.pop() else: while stack and operators[ch] <= operators[stack[-1]]: postfix.append(stack.pop()) stack.append(ch) while stack: postfix.append(stack.pop()) return postfix ``` 总结: 通过栈的使用,我们可以很方便地将一般算术表达式转换成后缀式,并且复杂度为O(n),非常高效。实际应用中,后缀表达式可以直接计算,比中缀表达式更便于计算。因此,这种算法也有很广泛的应用场景。 ### 回答3: 在编程领域中,算术表达式的转换是一项非常重要的任务。而在数据结构实验中,我们学习到了如何将一般算术表达式转换成后缀式。下面,我将从以下几个方面进行阐述。 一、算法思路 将一般算术表达式转换成后缀式的算法思路是将符号按照操作优先级依次入栈和出栈,这个过程需要遵守以下几个规则: 1、如果是操作数,直接输出 2、如果是左括号,入栈 3、如果是右括号,弹出栈中所有运算符,并将它们输出,直到遇到左括号为止,但是左括号不输出 4、如果是运算符,弹出所有优先级大于或等于该运算符的栈顶运算符,然后将该运算符入栈 5、最后,将栈中的所有运算符依次弹出并输出,不包括左括号 二、具体实现 在具体实现上,我们需要创建两个栈,一个用于存储运算符,一个用于输出后缀式,然后遍历算术表达式,根据上述算法思路将符号依次入栈和出栈即可。具体代码实现如下: void transform(char exp[], char postfix[]) { char ch, elem; int i = 0, k = 0; Stack<char> s; while ((ch = exp[i++]) != '\0') { if (ch == '(') s.push(ch); else if (isdigit(ch) || isalpha(ch)) { postfix[k++] = ch; } else if (ch == ')') { while ((elem = s.pop()) != '(') { postfix[k++] = elem; } } else { while (!s.isEmpty() && priority(s.peek()) >= priority(ch)) { postfix[k++] = s.pop(); } s.push(ch); } } while (!s.isEmpty()) { postfix[k++] = s.pop(); } postfix[k] = '\0'; } int priority(char ch) { if (ch == '(') return 0; if (ch == '+' || ch == '-') return 1; if (ch == '*' || ch == '/') return 2; return 3; } 三、算法的应用 将一般算术表达式转换成后缀式在编程中有着广泛的应用,例如表达式求值、编译器中的语法分析等。 最后,要注意的是,在转换过程中,应当考虑数据类型及精度等问题,并严格按照算法思路进行实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值