数据结构与算法(六)——逆波兰计算器

前缀、中缀、后缀表达式(逆波兰表达式)

前缀表达式(波兰表达式)

  1. 前缀表达式的运算符位于操作数之前,例如:(1+2)x(5+6)对应的前缀表达式为:+ x + 1 2 5 6
  2. 前缀表达式的计算机求值从右至左扫描表达式,遇到数字时,将数字入数栈,遇到运算符时,弹出数栈的两个数进行计算(因为是从右至左扫描,所以后入栈的数字在运算符前面,即栈顶的数字在前,次顶的数字灾在后),然后将结果入数栈,重复进行直到遍历完表达式,最后一次计算的结果就是表达式的值

中缀表达式

  1. 中缀表达式就是常见的运算表达式,如(1+2)x(5+6)
  2. 中缀表达式的求值对人来说很熟悉,但是对于计算机来说不好操作,因此在使用计算机计算结果时,通常先将表达式转成其他的表达式再进行求值(通常转成后缀表达式)

后缀表达式

  1. 后缀表达式又称逆波兰表达式,与前缀表达式相似,不过运算符在操作数之后,如(1+2)x(5+6) 的后缀表达式为:1 2 + 5 6 + x
  2. 后缀表达式的计算机求值从左至右扫描表达式,遇到数字时,将数字压入数栈,遇到符号时,弹出栈顶的两个数进行计算(因为是从左至右扫描,所以先入栈的数字在运算符前面,即次顶的数字在前,栈顶的数字在后),然后将结果入栈,依次直到遍历完表达式,最后的结果就是表达式的值

逆波兰计算器

中缀表达式转后缀表达式分析

  1. 初始化两个栈,一个栈用来存运算符s1一个栈用来存转换的中间结果s2
  2. 从左至右扫描表达式
  3. 遇到操作数时,将数压入结果栈s2
  4. 遇到运算符时,如果符号栈为空,或者栈顶为左括号"(",则直接压入符号栈;如果符号栈不为空,则比较当前符号和栈顶符号的优先级,如果当前符号的优先级更高则也压入符号栈,否则将符号栈中的符号弹出压入结果栈,然后继续对比符号栈的栈顶元素直到栈为空或优先级低于当前符号
  5. 遇到括号时,如果是左括号,直接入符号栈;如果是右括号,则依次弹出符号栈中的符号并压入结果栈中直到弹出左括号
  6. 遍历表达式结束后,依次将符号栈中的元素弹出并压入结果栈中
  7. 最后结果栈中的元素弹出的逆序就是该中缀表达式对应的后缀表达式

代码实现

/***
 * 逆波兰计算器
 * 
 * @author laowa
 *
 */
public class PolandCaculator {

	public static void main(String args[]) {
		System.out.println(caculate("1+((2+3)*4)-5*2"));
	}

	/**
	 * 中缀表达式转逆波兰表达式
	 * 
	 * @param infixExpression
	 *            待转的中缀表达式
	 * @return 后缀表达式
	 */
	private static List<String> conversion(String infixExpression) {
		// 创建符号栈
		Stack<String> operStack = new Stack<>();
		// 因为最终的结果栈需要逆序得到最终结果,所以使用集合代替栈省去了将结果逆序的操作
		List<String> result = new ArrayList<>();
		// 传入的中缀表达式的长度
		int length = infixExpression.length();
		// 当前遍历到的字符
		String current;
		// 开始遍历中缀表达式
		for (int i = 0; i < length; i++) {
			// 通过i来一个个截取字符
			current = infixExpression.substring(i, i + 1);
			// 当前为数字
			if (current.matches("\\d")) {
				// 将当前字符入结果栈
				result.add(current);
				// 查看后一位字符是否是数字,如果i==length-1表示当前已经是最后一位则不用查看,此时current指向的是下一个字符
				while (i != length - 1 && (current = infixExpression.substring(i + 1, i + 2)).matches("\\d")) {
					// 满足下一个字符也是数字时,将集合最后一个元素取出与改字符结合
					result.set(result.size() - 1, String
							.valueOf(Integer.parseInt(result.get(result.size() - 1)) * 10 + Integer.parseInt(current)));
					// 将i向后移动一位
					i++;
				}
			} else {
				// 当前为符号时
				if (operStack.isEmpty()) {
					// 符号栈为空则直接入符号栈
					operStack.push(current);
				} else {
					switch (current) {
					// 如果当前符号是左括号,则直接入栈
					case "(":
						operStack.push(current);
						break;
					// 如果当前符符号是右括号,则将栈中左括号之前的元素依次加入结果集
					case ")":
						while (!operStack.peek().equals("(")) {
							result.add(operStack.pop());
						}
						// 循环结束后栈顶为左括号,将这个左括号丢弃
						operStack.pop();
						break;
					// 如果当前符号是操作符
					default:
						while (!operStack.isEmpty()// 栈为空时跳出循环
								&& !operStack.peek().equals("(") // 栈顶为左括号时跳出循环
								&& Operation.getPriority(operStack.peek()) >= Operation.getPriority(current)) {// 栈顶元素优先级低于当前符号时,跳出循环
							result.add(operStack.pop());
						}
						// 跳出循环后表示当前要么栈空,要么栈顶为"("要么栈顶优先级低于当前符号,所以直接将当前符号入栈即可
						operStack.push(current);

						// if (operStack.peek().equals("(")) {
						// // 如果栈顶是左括号,则直接入栈
						// operStack.push(current);
						// } else if (priority(operStack.peek()) < priority(current)) {
						// // 如果当前符号优先级比栈顶的优先级要高,则当前符号入栈(因为后入栈的会先出栈,所以优先级高的符号会在优先级低的符号前计算)
						// operStack.push(current);
						// } else {
						// // 否则从符号栈中遍历,将所有优先级高于或等于当前符号的符号加入结果集(优先级相等但栈中元素在当前符号之前)
						// while (!operStack.isEmpty()// 栈为空时跳出循环
						// && !operStack.peek().equals("(") // 栈顶为左括号时跳出循环
						// && priority(operStack.peek()) >= priority(current)) {// 栈顶元素优先级低于当前符号时,跳出循环
						// result.add(operStack.pop());
						// }
						// // 将栈中优先级高的符号取完后,将当前符号入符号栈
						// operStack.push(current);
						// }
					}

				}
			}
		}
		// 表达式遍历完毕,将符号栈中的符号依次添加到结果集后面
		while (!operStack.isEmpty()) {
			result.add(operStack.pop());
		}
		return result;
	}

	/**
	 * 逆波兰表达式求值
	 * 
	 * @param suffixExpression
	 *            逆波兰表达式,为了方便每一个数字和符号之间使用空格隔开
	 * @return 结果
	 */
	public static int caculate(String suffixExpression) {
		// 创建数栈
		Stack<Integer> numStack = new Stack<>();
		// 遍历表达式
		for (String str : conversion(suffixExpression)) {
			// 使用正则表达式来匹配,当现在遍历到的字符串是一个数字时为true
			if (str.matches("\\d+")) {
				// 如果是数字则将数字入数栈
				numStack.push(Integer.parseInt(str));
			} else {
				// 符号则弹出数字栈中的两个数字进行计算,并将结果入栈
				numStack.push(cal(numStack.pop(), numStack.pop(), str));
			}
		}
		// 最后留在数栈中的数字就是表达式的结果
		return numStack.pop();
	}

	/**
	 * 计算
	 * 
	 * @param num1
	 *            数字栈中取出的第一个数字
	 * @param num2
	 *            数字栈中取出的第二个数字
	 * @param oper
	 *            操作符
	 * @return 运算结果
	 */
	private static int cal(int num1, int num2, String oper) {
		// 由于栈的先进后出特性,后出栈的数字应当在表达式的前面,所以减法和触发运算时,应当注意num2在前
		switch (oper) {
		case "+":
			return num1 + num2;
		case "-":
			return num2 - num1;
		case "*":
			return num1 * num2;
		case "/":
			return num2 / num1;
		}
		throw new RuntimeException("运算符有误");
	}

}

class Operation {
	private static final int ADD = 1;
	private static final int SUB = 1;
	private static final int MUL = 2;
	private static final int DIV = 2;

	/**
	 * 获取当前符号的优先级
	 * 
	 * @param opr
	 *            操作符
	 * @return 优先级
	 */
	public static int getPriority(String opr) {
		switch (opr) {
		case "+":
			return ADD;
		case "-":
			return SUB;
		case "*":
			return MUL;
		case "/":
			return DIV;
		default:
			throw new RuntimeException("输入有误");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值