栈的应用:表达式求值

栈(Stack):LIFO(后进先出)表

  1. 操作
    在这里插入图片描述

  2. 实现
    栈是一个表,因此任何实现表的方法都能实现栈。即栈是一个只对数组表或链表的末尾进行操作的模型。

  3. 应用:表达式求值
    对于原理,可以参考https://www.jianshu.com/p/762ab1825776

package 数据结构;

import java.util.Scanner;
import java.util.Stack;

public class StackTest {
	Stack<Character> OPS = new Stack<Character>();//符号栈
	Stack<Double> DIS = new Stack<Double>();//数字栈
	StringBuffer myEXP = new StringBuffer();//后缀表达式
	
	String exp;//中缀表达式

	static char oppriority(char Aop, char Bop) {//比较两个运算符的优先级
		char[][] OPP = { // 运算符优先等级 [栈顶] [当前]
/* |-------------------- 当 前 运 算 符 --------------------| */
/*            +    -    *    /    ^    !    (    )    # */
/* -- + */ { '>', '>', '<', '<', '<', '<', '<', '>', '>' },
/* |  - */ { '>', '>', '<', '<', '<', '<', '<', '>', '>' },
/* 栈 * */ { '>', '>', '>', '>', '<', '<', '<', '>', '>' },
/* 顶 / */ { '>', '>', '>', '>', '<', '<', '<', '>', '>' },
/* 运 ^ */ { '>', '>', '>', '>', '>', '<', '<', '>', '>' },
/* 算 ! */ { '>', '>', '>', '>', '>', '>', ' ', '>', '>' },
/* 符 ( */ { '<', '<', '<', '<', '<', '<', '<', '=', ' ' },
/* |  ) */ { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' },
/* -- # */ { '<', '<', '<', '<', '<', '<', '<', ' ', '=' } };
		int A_index = "+-*/^!()#".indexOf(Aop);
		int B_index = "+-*/^!()#".indexOf(Bop);
		return OPP[A_index][B_index];
	}

	boolean isOP(char ch) {//判符
		return "+-*/^!()#".indexOf(ch) != -1;
	}

	void inToSuffix() {//中缀转后缀
		int i = 0;//用作指针
		OPS.push('#');//先压入一个标识符,用于确认开始结尾
		//判负,一种情况时首字符为负号,另一种就是紧跟在左括号的后面的是负号,其他就是减号
		//对首符负号的处理,当作最后用0减去计算结果
		if (exp.charAt(i) == '-') { // 目前i=0,判断首位是不是负号
			myEXP.append('0');
		}
		while (i < exp.length()) {//读取到表达式完结
			char ch = exp.charAt(i);
			if (ch == ' ') {//遇到空格跳过,读取下一字符个
				i++;
				continue;
			}
			if (!isOP(ch)) {//如果不是符号(即数字),加到后缀表达式中
				myEXP.append(ch);
				i++;
			} else {//是符号
			//紧跟在左括号后面的是负号,当作0-该数处理
				if (ch == '(' && exp.charAt(i + 1) == '-') {
					myEXP.append('0');
				}
				//每读到一个符号加一个空格,中缀表达式中两个数中间必然有一个符号
				//不假空格的话计算后缀表达式中数字将粘成一串,无法解析多位数
				myEXP.append(' ');
				//比较栈顶元素与当前元素优先级
				char lv = oppriority(OPS.peek(), ch);
				switch (lv) {
				case '<'://<当,当入栈,读取下一字符
					OPS.push(ch);
					i++;
					break;
				case '>'://>当,顶出栈追加到后缀表达式,当继续比较
					myEXP.append(OPS.pop());
					break;
				case '='://只有右括号对左括号为=,将括号弹出,读下一字符
					OPS.pop();
					i++;
					break;
				default:
					throw new IllegalArgumentException("表达式错误 ");
				}
			}
		}
		while (!OPS.isEmpty())//表达式解析完成后,将剩余运算符全部弹出
			myEXP.append(OPS.pop());
	}

	double evaluate() {//计算后缀表达式的值
		double value = 0;//存值
		int start = 0;//读多位数的起始坐标
		double a = 0;//先弹出的元素
		double b = 0;//后弹出的元素
		//对于一个式子 9-3,转化为后缀为 9 3 -,则a=3,b=9,直接a-b的值是错误的,
		//对于+ -,顺序不影响结果,对于- / !^而言,顺序对结果是有影响的,故需调换顺序
		for (int k = 0; k < myEXP.length(); k++) {
			char c = myEXP.charAt(k);
			switch (c) {
			case '+':
				value = DIS.pop() + DIS.pop();
				DIS.push(value);
				break;
			case '-':
				a = DIS.pop();
				b = DIS.pop();
				value = b - a;
				DIS.push(value);
				break;
			case '*':
				value = DIS.pop() * DIS.pop();
				DIS.push(value);
				break;
			case '/':
				a = DIS.pop();
				b = DIS.pop();
				value = b / a;
				DIS.push(value);
				break;
			case '^':
				a = DIS.pop();
				b = DIS.pop();
				value = Math.pow(b, a);
				DIS.push(value);
				break;
			case '!':
				value = factorial(DIS.pop());
				DIS.push(value);
				break;
			case '#':
				return DIS.pop();
			case ' ':
				break;
			default:
				//对于多位数的读取,一个数必然前面是空格,后面则是运算符或空格,故需进行判断
				//后缀表达式的首字符必然是数字
				if (k > 0) {
					if (myEXP.charAt(k - 1) == ' ') {
						start = k;
					}
				}
				if ("+-*/^!()# ".indexOf(myEXP.charAt(k + 1)) != -1) {
					DIS.push(Double.valueOf(myEXP.substring(start, k + 1)));
				}
				break;

			}
		}

		throw new IllegalArgumentException("表达式错误 ");

	}

	double factorial(double num) {//阶乘运算
		int  sum = 1;
		while ((int)num > 1) {
			sum *= num;
			num--;
		}
		return (double)sum;
	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		StackTest st = new StackTest();
		st.exp = sc.nextLine();
		st.inToSuffix();
		System.out.println(st.myEXP);
		System.out.println(st.evaluate());
	}

}

  1. 测试用例及结果
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
发布了41 篇原创文章 · 获赞 1 · 访问量 1477

猜你喜欢

转载自blog.csdn.net/qq_44467578/article/details/103965174