栈应用-中缀表达式转后缀表达式并计算值
表达式的三种形式:
中缀表达式:运算符放在两个运算对象中间,如:(2+1)*3。我们从小做数学题时,一直使用的就是中缀表达式。
后缀表达式:不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则),如:2 1 + 3 。又比如3+(6-4/2)5=23的后缀表达式为:3642/-5+# (#符号为结束符)
前缀表达式:同后缀表达式一样,不包含括号,运算符放在两个运算对象的前面,如: + 2 1 3 。前缀表达式和后缀表达式其实是差不多的,只不过符号位置不同而已,前缀表达式不是很常见。
中缀转后缀表达式
有两个栈:运算符栈和操作数栈
简述:左括号直接压入,压入右括号,则要一直弹出到左括号(左括号舍弃)。
压入低运算符到【运算符栈】的时候, 先将高于/等于该运算符级别的【运算符栈】栈顶运算符弹出。
- 从左到右扫描
- 若读到是操作数,则判断操作数的类型,并将操作数存入【操作数栈】
- 若读到的是运算符
3.1 若为左括号“(”,则直接存入【运算符栈】
3.2 若为右括号“)”,则弹出【运算符栈】的运算符,直到遇到左括号为止,此时抛弃该左括号。
3.3 运算符为非括号运算符
a. 若【运算符栈】栈顶的运算符为左括号,则直接存入【运算符栈】
b. 若比【运算符栈】栈顶的运算符优先级高,则直接存入【运算符栈】。
c. 若比【运算符栈】栈顶的运算符优先级低或相等,则输出栈顶运算符到【操作数栈】,直到【运算符栈】栈顶运算符低于(不包括等于)该运算符优先级或为左括号,并将当前运算符压入【运算符栈】
ps:从【运算符】栈顶依次弹出所有【高于或等于】当前运算符的运算符,遇到左括号或低于该当前运算符的则停止,并将当前运算符压入【运算符栈】。 - 当读取完成后运算符栈中尚有运算符时,则依序取出运算符到【操作数栈】直到【运算符栈】为空。
代码
package com.gkwind.structure.stack;
import java.util.NoSuchElementException;
/**
* 四则运算
* 队列实现操作数栈,栈实现运算符栈
*
* @Author thewindkee
* @Date 2019/7/9 0009 下午 2:16
*/
public class MathOperation2 {
public MathOperation2() {
}
public Integer calculate(String expr) {
LinkedQueue<String> numStack = mapToNumStack(expr);
System.out.println("numStack"+numStack);
int result = exec(numStack);
return result;
}
/**
* 遍历后缀表达式
* 将数字压栈,遇到操作符则弹出栈顶的两个元素
*
* @param numStack
* @return
*/
private int exec(LinkedQueue<String> numStack) {
//遇到操作符则弹出栈顶的两个元素
if (numStack == null || numStack.size() < 3) {
throw new IllegalArgumentException();
}
Stack<Integer> stack = new Stack<>();
String item;
while (numStack.size()>0) {
item = numStack.removeFirst();
if (isDigits(item)) {
stack.push(Integer.parseInt(item));
}else{
Integer b = stack.pop();
Integer a = stack.pop();
//计算栈顶的两个元素,并将结果压栈
stack.push(MathOpt.getOpt(item.charAt(0)).calculate(a, b));
}
}
return stack.pop();
}
private boolean isDigits(String item) {
if (item == null || item.trim().equals("")) {
return false;
}
for (int i = 0; i < item.length(); i++) {
if (!Character.isDigit(item.charAt(i))) {
return false;
}
}
return true;
}
private LinkedQueue<String> mapToNumStack(String expr) {
Stack<Character> optStack = new Stack<Character>();
LinkedQueue<String> numQueue = new LinkedQueue<String>();
//1.从左到右扫描
boolean numEnd = false;
StringBuilder tempNum = new StringBuilder();
for (int i = 0; i < expr.length(); i++) {
final char c = expr.charAt(i);
if (Character.isDigit(c)) {
//2.若读到是操作数,先存在tempNum中
tempNum.append(c);
// numQueue.addLast(String.valueOf(c));
}else{
//3.若读到的是运算符
//tempNum是否有数字,一并存入【操作数栈】
if (tempNum.length()>0) {
numQueue.addLast(tempNum.toString());
tempNum.delete(0, tempNum.length());
}
MathOpt mathOpt = MathOpt.getOpt(c);
dealMathOpt(mathOpt, optStack, numQueue);
}
}
if (tempNum.length()>0) {
numQueue.addLast(tempNum.toString());
tempNum.delete(0, tempNum.length());
}
//4.当表示读取完成后运算符栈中尚有运算符时,则依序取出运算符到【操作数栈】直到【运算符栈】为空。
while (optStack.size() != 0) {
numQueue.addLast(String.valueOf(optStack.pop()));
}
return numQueue;
}
private void dealMathOpt(MathOpt currentMathOpt, final Stack<Character> optStack, final LinkedQueue<String> numQueue) {
if (currentMathOpt == null || optStack == null || numQueue == null) {
throw new NullPointerException();
}
//3.若读到的是运算符
Character headOptChar = optStack.peek();
MathOpt headMathOpt = headOptChar==null?null: MathOpt.getOpt(headOptChar);
if(currentMathOpt.priority == Priority.HIGH){//括号运算符
if (currentMathOpt == MathOpt.LEFT_BRACKET) {
//3.1 若为左括号“(”,则直接存入【运算符栈】
optStack.push(currentMathOpt.opt);
}else{
//3.2 若为右括号“)”,则输出【运算符栈】的运算符,直到遇到左括号为止,此时抛弃该左括号。
while (true) {
Character nextOptChar = optStack.peek();
if (nextOptChar == null) {
throw new IllegalArgumentException("缺少左括号");
}
MathOpt nextHeadMathOpt = MathOpt.getOpt(nextOptChar);
if (nextHeadMathOpt.equals(MathOpt.LEFT_BRACKET)) {
//栈顶为左括号 ,丢弃
optStack.pop();
break;
}
numQueue.addLast(String.valueOf(optStack.pop()));
}
}
}else {//非括号运算符
if (headMathOpt!=null && MathOpt.LEFT_BRACKET.opt == headMathOpt.opt) {
//a.若【运算符栈】栈顶的运算符为左括号,则直接存入【运算符栈】
optStack.push(currentMathOpt.opt);
} else if (headMathOpt==null||currentMathOpt.priority.ordinal()> headMathOpt.priority.ordinal()) {
//b.若比【运算符栈】栈顶的运算符优先级高,则直接存入【运算符栈】。
optStack.push(currentMathOpt.opt);
} else {
// ps:从【运算符】栈顶依次弹出所有【高于或等于】当前运算符的运算符,遇到左括号或低于该当前运算符的则停止。
//c.若比【运算符栈】栈顶的运算符优先级低或相等,则输出栈顶运算符到【操作数栈】,
// 直到【运算符栈】栈顶运算符低于(不包括等于)该运算符优先级或为左括号,并将当前运算符压入【运算符栈】
numQueue.addLast(String.valueOf(optStack.pop()));
while (true) {
Character nextOptChar = optStack.peek();
if (nextOptChar == null) {
//栈顶已经不存在运算,将当前运算法压入【运算符栈】
optStack.push(currentMathOpt.opt);
break;
}
MathOpt nextHeadMathOpt = MathOpt.getOpt(nextOptChar);
if (nextHeadMathOpt.equals(MathOpt.LEFT_BRACKET)) {
//栈顶为左括号 ,停止
//当前运算法压入【运算符栈】
optStack.push(currentMathOpt.opt);
break;
}else if(currentMathOpt.priority.ordinal() <= nextHeadMathOpt.priority.ordinal()){
//比栈顶运算符小的,输出到【操作数栈】
numQueue.addLast(String.valueOf(optStack.pop()));
}else{
//比栈顶运算符大的,将当前运算符压入【运算符栈】,并退出
optStack.push(currentMathOpt.opt);
break;
}
}
}
}
}
enum MathOpt {
ADD(Priority.LOW, '+'){
@Override
int calculate(int a, int b) {
return a+b;
}
},MINUS(Priority.LOW,'-')
{
@Override
int calculate(int a, int b) {
return a-b;
}
},MULTI(Priority.MIDDLE,'*')
{
@Override
int calculate(int a, int b) {
return a*b;
}
},DIV(Priority.MIDDLE,'/'){
@Override
int calculate(int a, int b) {
return a/b;
}
},
LEFT_BRACKET(Priority.HIGH, '('){
@Override
int calculate(int a, int b) {
throw new IllegalArgumentException();
}
}, RIGHT_BRACKET(Priority.HIGH, ')'){
@Override
int calculate(int a, int b) {
throw new IllegalArgumentException();
}
},;
private final Priority priority;
private final char opt;
MathOpt(Priority priority, char opt) {
this.priority=priority;
this.opt = opt;
}
static MathOpt getOpt(char opt) {
for (MathOpt mathOpt : MathOpt.values()) {
if (mathOpt.opt == opt) {
return mathOpt;
}
}
return null;
}
abstract int calculate(int a, int b);
}
enum Priority{
LOW(),MIDDLE(),HIGH();
// LOW(1),MIDDLE(2),HIGH(3);
// private final int level;
// Priority(int i) {
// this.level = i;
// }
}
static class LinkedQueue<E> {
private Stack.Node<E> head;
private Stack.Node<E> tail;
private int count=0;
private static class Node<E> {
E data;
Stack.Node<E> prev;
Stack.Node<E> next;
public Node(E data, Stack.Node<E> prev, Stack.Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"data\":")
.append(data);
sb.append(",\"prev\":")
.append(prev);
sb.append(",\"next\":")
.append(next);
sb.append('}');
return sb.toString();
}
}
public <E> LinkedQueue() {
}
public void addLast(E e) {
if (tail == null) {
head = tail = new Stack.Node<>(e, null, null);
}else{
Stack.Node<E> node = new Stack.Node<>(e, null, null);
tail.next=node;
node.prev = tail;
tail = node;
}
count++;
}
public E removeFirst() {
checkNotEmpty();
final Stack.Node<E> headNode = this.head;
final E data = headNode.data;
Stack.Node<E> next = head.next;
head=next;
count--;
//help gc
if(next!=null)next.prev=null;
headNode.data=null;
headNode.next=null;
return data;
}
private void checkNotEmpty() {
if (count == 0) {
throw new NoSuchElementException();
}
}
public int size(){
return count;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("size: ").append(size()).append(",data:[");
Stack.Node<E> node = head;
while (node != null) {
sb.append(node.data);
node = node.next;
if (node == null) {
break;
}
sb.append(",");
}
sb.append("]}");
return sb.toString();
}
}
public static void main(String[] args) {
LinkedQueue<Integer> queue = new LinkedQueue<>();
queue.addLast(3);
queue.addLast(4);
queue.addLast(5);
// queue.addLast(7);//exception
System.out.println(queue);
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println(queue.removeFirst());
System.out.println(queue);
// System.out.println(queue.removeFirst());//exception
// System.out.println(queue);
//test stack
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
// stack.push(4);//
System.out.println(stack);
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack);
// System.out.println(stack.pop());//exception
System.out.println(new MathOperation2().calculate("9+(3-1)*3+10/2")==9+(3-1)*3+10/2);
System.out.println(new MathOperation2().calculate("(1+2)*3-4*5/2")==(1+2)*3-4*5/2);
System.out.println(new MathOperation2().calculate("(1+2)*(5*(3-4)*2)*5/4")==(1+2)*(5*(3-4)*2)*5/4);
}
/**
* 先进后出
* @param <E>
*/
static class Stack<E>{
private Node<E> head;
private Node<E> tail;
private int count=0;
// private int capacity;
private static class Node<E>{
E data;
Node<E> prev;
Node<E> next;
public Node(E data,Node<E> prev, Node<E> next) {
this.data = data;
this.prev = prev;
this.next = next;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"data\":")
.append(data);
sb.append(",\"prev\":")
.append(prev);
sb.append(",\"next\":")
.append(next);
sb.append('}');
return sb.toString();
}
}
public Stack() {
}
public void push(E e) {
// checkNotFull();
count ++;
if (tail == null) {
head = tail = new Node<>(e,null, null);
}else{
Node<E> node = new Node<>(e, null, null);
tail.next = node;
node.prev = tail;
//newNode become tail Node
tail = node;
}
}
// private void checkNotFull() {
// if(count==capacity)throw new IndexOutOfBoundsException();
// }
private void checkNotEmpty() {
if(count==0) throw new NoSuchElementException();
}
public E peek() {
return tail==null?null:tail.data;
}
public E pop() {
checkNotEmpty();
final Node<E> tailNode = tail;
Node<E> prev = tailNode.prev;
final E data = tailNode.data;
tailNode.prev = null;
if (prev == null) {
//tailNode is headNode
head = null;
} else {
prev.next = null;
}
//help gc
tailNode.prev = null;
tailNode.data = null;
tail = prev;
count--;
return data;
}
public int size(){
return count;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("size:").append(count).append(",data:[");
Node<E> node = head;
while (node!=null) {
sb.append(node.data);
node=node.next;
if (node == null) {
break;
}
sb.append(",");
}
sb.append("]}");
return sb.toString();
}
}
}