表達式的三種形式:
中綴表達式:運算符放在兩個運算對象中間,如:(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();
}
}
}