逆波兰计算器是栈的一种运用方式,一个很简单的式子1+((2+3)*4)-5,心算都能很快算出来,这种式子叫做中缀表达式,我们算起来很方便,但是计算机算起来呢很不方便,有一种计算机很方便计算的方式,叫做后缀表达式,也叫逆波兰表达式。
逆波兰表达式是波兰逻辑学家J・卢卡西维兹(J・ Lukasewicz)于1929年首先提出的一种表达式的表示方法。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
先把1+((2+3)*4)-5的逆波兰表达式写出来:123+4*+5-,这种如何算呢,遇到运算符就将前两个数进行运算然后将结果放回原位置,比如第一个遇到加号,就将2+3=5,将5放回,然后遇到*,5*4=20,将20放回,然后遇到+号,1+20=21,将21放回,然后遇到-,21-5=16,16就是最后的答案,这个过程写起来倒也简单,只要把后缀表达式存入数组,或者倒叙存入栈中,然后一个个读取,只要读取到不是数字,就让前两个数字进行运算就可以了。
代码如下:
//创建计算后缀表达式的栈
ReversePolishLinkedListStack stack = new ReversePolishLinkedListStack();
while (true) {
String s = stacks1.pop();//stacks1是存储这后缀表达式逆序的栈
if (stack.isOper(s)) {//是否是运算符
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int cal = stack.cal(num1, num2, s);//加减乘除计算方法
stack.push(String.valueOf(cal));//入栈
} else {
stack.push(s);
}
if (stacks1.isEmpty()) {//是否栈空
break;
}
}
后缀表达式的计算我们算好了,接下来就是最重要的一步了,中缀表达式转后缀表达式,具体的步骤呢,我看别人这样写的我就直接粘贴下来了,让我想我也想不出来。过程如下
1) 初始化两个栈:运算符栈s1和储存中间结果的栈 s2;
2) 从左至右扫描中缀表达式;
3) 遇到操作数时,将其压s2;
4) 遇到运算符时,比较其与s1栈顶运算符的优先级:
1.如果s1为空,或栈顶运算符为左括号“(”,则直接将此运算符入栈;
2.否则,若优先级比栈顶运算符的高,也将运算符压入s1;
3.否则,将s1栈顶的运算符弹出并压入到 s2 中再次转到(4-1)与s1中新的栈顶运算符相比较;
5) 遇到括号时:(1) 如果是左括号“(”,则直接压入 s1
(2) 如果是右括号“)”,则依次弹出 s1 栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃 。
6) 重复步骤2至5,直到到中缀表达式的最右边。
7) 将s1中剩余的运算符依次弹出并压入 s2 。
8) 依次弹出s2中的元素并输出,结果的逆序即为中缀表达式对应的后缀表达式。
代码如下:(代码中标的圆圈123对应的就是上面的步骤)
package datastructure.stack;
import com.sun.deploy.util.StringUtils;
public class ReversePolishExpressionDemo {
public static void main(String[] args) {
//中缀转后缀 ①
ReversePolishLinkedListStack stacks1 = new ReversePolishLinkedListStack();
ReversePolishLinkedListStack stacks2 = new ReversePolishLinkedListStack();
//"1", "2", "3", "+", "4", "*", "5", "-", "+"
String infix = "1+((2+3)*4)-5"; //中缀表达式
int index = 0; //记录当前扫描到哪个数字了
String temporary = "";
int expression_index = 0;
//将中缀表达式转换为后缀表达式
while (true) {
if (Character.isDigit(infix.charAt(index))) {//是数字 ②
temporary += infix.charAt(index);
if (index + 1 < infix.length()) {//如果不是最后一个字符
index++;
} else {
stacks2.push(temporary);
break;//结束循环
}
if (!Character.isDigit(infix.charAt(index))) {//下一个不是数字
stacks2.push(temporary);//入栈s2
temporary = "";//临时字符串置空
}
}
if (!Character.isDigit(infix.charAt(index))) {//不是数字 ③
if (stacks1.isEmpty()) {
stacks1.push(String.valueOf(infix.charAt(index)));
index++;
} else {
char currentoper = (char) infix.charAt(index);
int current = stacks1.priority(String.valueOf(infix.charAt(index)));//当前扫描的优先级
int peek = stacks1.priority(stacks1.peek());//栈顶优先级
String peekoper = stacks1.peek();//栈顶符号是什么
while (true) {
if (stacks1.isEmpty() || '(' == currentoper) {//是左括号 ⑤
stacks1.push(String.valueOf(infix.charAt(index)));
index++;
break;
} else if (currentoper == ')') {//如果遇到右括号 ⑤ 弹出s1栈顶 压入s2 直到遇到左括号
if (!peekoper.equals("(")) {
while (true) {
String pop = stacks1.pop();
stacks2.push(pop);
if (stacks1.peek().equals("(")) {
stacks1.pop();
index++;
break;
}
}
break;
}
} else if (current > peek) { // ④
stacks1.push(String.valueOf(infix.charAt(index)));
index++;
break;
} else {
//将s1栈顶弹出,加入s2 然后与新栈顶继续比较
String pop = stacks1.pop();
stacks2.push(pop);
}
}
}
}
if (index >= infix.length()) { //⑥
break;
}
}
while (true) {//将s1的依次弹出加入s2 ⑦
if (stacks1.isLast()) {
String pop = stacks1.pop();
stacks2.push(pop);
break;
}
String pop = stacks1.pop();
stacks2.push(pop);
}
//逆序一下 转换成后缀表达式 ⑧
while (true) {
if (stacks2.isLast()) {
String pop = stacks2.pop();
stacks1.push(pop);
break;
}
String pop = stacks2.pop();
stacks1.push(pop);
}
stacks1.list();
System.out.println("===========================");
ReversePolishLinkedListStack stack = new ReversePolishLinkedListStack();//创建计算后缀表达式的栈
while (true) {
String s = stacks1.pop();//stacks1是存储这后缀表达式逆序的栈
if (stack.isOper(s)) {//是否是运算符
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int cal = stack.cal(num1, num2, s);//加减乘除计算方法
stack.push(String.valueOf(cal));//入栈
} else {
stack.push(s);
}
if (stacks1.isEmpty()) {//是否栈空
break;
}
}
System.out.println(infix + "=" + stack.peek());
}
}
class ReversePolishNode {
String data;
ReversePolishNode next;
}
class ReversePolishLinkedListStack {
ReversePolishNode head = new ReversePolishNode();
//入栈-push
public void push(String value) {
ReversePolishNode xyg = new ReversePolishNode();
xyg.data = value;
xyg.next = head.next;
head.next = xyg;
}
//栈空
public boolean isEmpty() {
if (head.next == null) {
return true;
}
return false;
}
//只剩一个
public boolean isLast() {
if (head.next.next == null) {
return true;
}
return false;
}
//查看栈首元素
public String peek() {
if (isEmpty()) {
throw new RuntimeException("栈空,没有数据。");
}
return head.next.data;
}
//出栈-pop
public String pop() {
//判断栈是否空
if (isEmpty()) {
throw new RuntimeException("栈空,没有数据。");
}
String result = head.next.data;
head.next = head.next.next;
return result;
}
//遍历
public void list() {
if (isEmpty()) {
System.out.println("栈空,没有数据。");
}
ReversePolishNode temp = head;
while (temp.next != null) {
System.out.print(temp.next.data);
temp = temp.next;
}
System.out.println();
}
//返回运算符的优先级,优先级是程序员来确定,优先级使用数字表示
//数字越大,则优先级就越高。
public int priority(String oper) {
if (oper.equals("*") || oper.equals("/")) {
return 1;
} else if (oper.equals("+") || oper.equals("-")) {
return 0;
} else {
return -1;//只支持+-*/四种运算符
}
}
//判断是不是一个运算符
public boolean isOper(String val) {
return val.equals("+") || val.equals("-") || val.equals("*") || val.equals("/");
}
//计算方法
public int cal(int num1, int num2, String oper) {
int res = 0;
switch (oper) {
case "+":
res = num1 + num2;
break;
case "-":
res = num2 - num1;
break;
case "*":
res = num1 * num2;
break;
case "/":
res = num2 / num1;
break;
default:
break;
}
return res;
}
}