栈(Stack):LIFO(后进先出)表
-
操作
-
实现
栈是一个表,因此任何实现表的方法都能实现栈。即栈是一个只对数组表或链表的末尾进行操作的模型。 -
应用:表达式求值
对于原理,可以参考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());
}
}
- 测试用例及结果