棧的一個(gè)實(shí)際需求
請(qǐng)輸入一個(gè)表達(dá)式
計(jì)算式:[722-5+1-5+3-3] 點(diǎn)擊計(jì)算【如下圖】
需求圖
請(qǐng)問: 計(jì)算機(jī)底層是如何運(yùn)算得到結(jié)果的? 注意不是簡(jiǎn)單的把算式列出運(yùn)算,因?yàn)槲覀兛催@個(gè)算式 7 * 2 * 2 - 5, 但是計(jì)算機(jī)怎么理解這個(gè)算式的(對(duì)計(jì)算機(jī)而言,它接收到的就是一個(gè)字符串),討論的是這個(gè)問題。-> 棧
棧的介紹
棧的英文為(stack)
1.棧是一個(gè)先入后出(FILO-First In Last Out)的有序列表。
2.棧(stack)是限制線性表中元素的插入和刪除只能在線性表的同一端進(jìn)行的一種特殊線性表。允許插入和刪除的一端,為變化的一端,稱為棧頂(Top),另一端為固定的一端,稱為棧底(Bottom)。
3.根據(jù)棧的定義可知,最先放入棧中元素在棧底,最后放入的元素在棧頂,而刪除元素剛好相反,最后放入的元素最先刪除,最先放入的元素最后刪除
4.出棧(pop)和入棧(push)的概念(如圖所示)
棧的示意圖
棧的示意圖
棧的應(yīng)用場(chǎng)景
1.子程序的調(diào)用:在跳往子程序前,會(huì)先將下個(gè)指令的地址存到堆棧中,直到子程序執(zhí)行完后再將地址取出,以回到原來的程序中。
2.處理遞歸調(diào)用:和子程序的調(diào)用類似,只是除了儲(chǔ)存下一個(gè)指令的地址外,也將參數(shù)、區(qū)域變量等數(shù)據(jù)存入堆棧中。
3.表達(dá)式的轉(zhuǎn)換[中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式]與求值(實(shí)際解決)。
4.二叉樹的遍歷。
5.圖形的深度優(yōu)先(depth一first)搜索法。
棧的快速入門
用數(shù)組模擬棧的使用,由于棧是一種有序列表,當(dāng)然可以使用數(shù)組的結(jié)構(gòu)來儲(chǔ)存棧的數(shù)據(jù)內(nèi)容,下面用數(shù)組模擬棧的出棧,入棧等操作。
實(shí)現(xiàn)思路分析,并畫出示意圖

使用數(shù)組模擬棧
package cn.icanci.datastructure.stack;
import sun.nio.cs.FastCharsetProvider;
import java.util.Scanner;
/**
* @Author: icanci
* @ProjectName: AlgorithmAndDataStructure
* @PackageName: cn.icanci.datastructure.stack
* @Date: Created in 2020/3/2 13:04
* @ClassAction: 使用數(shù)組來模擬棧
*/
public class ArrayStackDemo {
public static void main(String[] args) {
//測(cè)試
//先創(chuàng)建一個(gè)ArrayStack
ArrayStack arrayStack = new ArrayStack(5);
String key = "";
boolean loop = true;
Scanner sc = new Scanner(System.in);
while (loop) {
System.out.println();
System.out.println("show:表示顯示棧");
System.out.println("exit:表示退出程序");
System.out.println("push:表示入站");
System.out.println("pop:表示出戰(zhàn)");
key = sc.nextLine();
switch (key) {
case "show":
arrayStack.list();
break;
case "exit":
loop = false;
System.out.println("退出");
break;
case "push":
System.out.print("請(qǐng)輸入一個(gè)數(shù)字:");
int value = sc.nextInt();
arrayStack.push(value);
break;
case "pop":
System.out.println(arrayStack.pop());
break;
default:
break;
}
}
}
}
//定義一個(gè)ArrayStack模擬戰(zhàn)
class ArrayStack {
//棧的大小
private int maxSize;
//數(shù)組模擬棧
private int[] stack;
//top表示棧頂 初始話為-1
private int top = -1;
//構(gòu)造器
public ArrayStack(int maxSize) {
this.maxSize = maxSize;
stack = new int[maxSize];
}
//棧滿了
public boolean isFull() {
return top == maxSize - 1;
}
//棧空
public boolean isEmpty() {
return top == -1;
}
//入站
public void push(int value) {
//判斷棧滿
if (isFull()) {
System.out.println("棧滿");
return;
}
top++;
stack[top] = value;
}
//出棧
public int pop() {
//判斷??? if (isEmpty()) {
System.out.println("???);
throw new RuntimeException("???);
}
return stack[top--];
}
//遍歷棧
public void list() {
if (isEmpty()) {
System.out.println("棧空");
return;
}
for (int i = top; i >= 0; i--) {
System.out.println("stack[" + i + "]:" + stack[i]);
}
}
}
使用棧實(shí)現(xiàn)綜合計(jì)算器
使用棧來實(shí)現(xiàn)綜合計(jì)算器-自定義優(yōu)先級(jí)[priority]

簡(jiǎn)化思路:
3+26-2
30+26-2
722-5+1-5+3-4
思路
使用棧完成表達(dá)式的計(jì)算 思路
- 通過一個(gè) index 值(索引),來遍歷我們的表達(dá)式
- 如果我們發(fā)現(xiàn)是一個(gè)數(shù)字, 就直接入數(shù)棧
- 如果發(fā)現(xiàn)掃描到是一個(gè)符號(hào), 就分如下情況
3.1 如果發(fā)現(xiàn)當(dāng)前的符號(hào)棧為 空,就直接入棧
3.2 如果符號(hào)棧有操作符,就進(jìn)行比較,如果當(dāng)前的操作符的優(yōu)先級(jí)小于或者等于棧中的操作符, 就需要從數(shù)棧中pop出兩個(gè)數(shù),在從符號(hào)棧中pop出一個(gè)符號(hào),進(jìn)行運(yùn)算,將得到結(jié)果,入數(shù)棧,然后將當(dāng)前的操作符入符號(hào)棧, 如果當(dāng)前的操作符的優(yōu)先級(jí)大于棧中的操作符, 就直接入符號(hào)棧.- 當(dāng)表達(dá)式掃描完畢,就順序的從 數(shù)棧和符號(hào)棧中pop出相應(yīng)的數(shù)和符號(hào),并運(yùn)行.
- 最后在數(shù)棧只有一個(gè)數(shù)字,就是表達(dá)式的結(jié)果
驗(yàn)證: 3+2*6-2 = 13
package cn.icanci.datastructure.stack;
/**
* @Author: icanci
* @ProjectName: AlgorithmAndDataStructure
* @PackageName: cn.icanci.datastructure.stack
* @Date: Created in 2020/3/2 18:14
* @ClassAction: 計(jì)算器的棧實(shí)現(xiàn)
*/
public class Calculator {
public static void main(String[] args) {
String expression = "7*2*2-5+1-5+3-4"; // 15//如何處理多位數(shù)的問題?
//創(chuàng)建兩個(gè)棧,數(shù)棧,一個(gè)符號(hào)棧
ArrayStack2 numStack = new ArrayStack2(10);
ArrayStack2 operStack = new ArrayStack2(10);
//定義需要的相關(guān)變量
int index = 0;//用于掃描
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' '; //將每次掃描得到char保存到ch
String keepNum = ""; //用于拼接 多位數(shù)
//開始while循環(huán)的掃描expression
while(true) {
//依次得到expression 的每一個(gè)字符
ch = expression.substring(index, index+1).charAt(0);
//判斷ch是什么,然后做相應(yīng)的處理
if(operStack.isOper(ch)) {//如果是運(yùn)算符
//判斷當(dāng)前的符號(hào)棧是否為空
if(!operStack.isEmpty()) {
//如果符號(hào)棧有操作符,就進(jìn)行比較,如果當(dāng)前的操作符的優(yōu)先級(jí)小于或者等于棧中的操作符,就需要從數(shù)棧中pop出兩個(gè)數(shù),
//在從符號(hào)棧中pop出一個(gè)符號(hào),進(jìn)行運(yùn)算,將得到結(jié)果,入數(shù)棧,然后將當(dāng)前的操作符入符號(hào)棧
if(operStack.priority(ch) <= operStack.priority(operStack.peek())) {
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
//把運(yùn)算的結(jié)果如數(shù)棧
numStack.push(res);
//然后將當(dāng)前的操作符入符號(hào)棧
operStack.push(ch);
} else {
//如果當(dāng)前的操作符的優(yōu)先級(jí)大于棧中的操作符, 就直接入符號(hào)棧.
operStack.push(ch);
}
}else {
//如果為空直接入符號(hào)棧..
operStack.push(ch); // 1 + 3
}
} else { //如果是數(shù),則直接入數(shù)棧
//numStack.push(ch - 48); //? "1+3" '1' => 1
//分析思路
//1. 當(dāng)處理多位數(shù)時(shí),不能發(fā)現(xiàn)是一個(gè)數(shù)就立即入棧,因?yàn)樗赡苁嵌辔粩?shù)
//2. 在處理數(shù),需要向expression的表達(dá)式的index 后再看一位,如果是數(shù)就進(jìn)行掃描,如果是符號(hào)才入棧
//3. 因此我們需要定義一個(gè)變量 字符串,用于拼接
//處理多位數(shù)
keepNum += ch;
//如果ch已經(jīng)是expression的最后一位,就直接入棧
if (index == expression.length() - 1) {
numStack.push(Integer.parseInt(keepNum));
}else{
//判斷下一個(gè)字符是不是數(shù)字,如果是數(shù)字,就繼續(xù)掃描,如果是運(yùn)算符,則入棧
//注意是看后一位,不是index++
if (operStack.isOper(expression.substring(index+1,index+2).charAt(0))) {
//如果后一位是運(yùn)算符,則入棧 keepNum = "1" 或者 "123"
numStack.push(Integer.parseInt(keepNum));
//重要的!!!!!!, keepNum清空
keepNum = "";
}
}
}
//讓index + 1, 并判斷是否掃描到expression最后.
index++;
if (index >= expression.length()) {
break;
}
}
//當(dāng)表達(dá)式掃描完畢,就順序的從 數(shù)棧和符號(hào)棧中pop出相應(yīng)的數(shù)和符號(hào),并運(yùn)行.
while(true) {
//如果符號(hào)棧為空,則計(jì)算到最后的結(jié)果, 數(shù)棧中只有一個(gè)數(shù)字【結(jié)果】
if(operStack.isEmpty()) {
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1, num2, oper);
numStack.push(res);//入棧
}
//將數(shù)棧的最后數(shù),pop出,就是結(jié)果
int res2 = numStack.pop();
System.out.printf("表達(dá)式 %s = %d", expression, res2);
}
}
//先創(chuàng)建一個(gè)棧,直接使用前面創(chuàng)建好
//定義一個(gè) ArrayStack2 表示棧, 需要擴(kuò)展功能
class ArrayStack2 {
private int maxSize; // 棧的大小
private int[] stack; // 數(shù)組,數(shù)組模擬棧,數(shù)據(jù)就放在該數(shù)組
private int top = -1;// top表示棧頂,初始化為-1
//構(gòu)造器
public ArrayStack2(int maxSize) {
this.maxSize = maxSize;
stack = new int[this.maxSize];
}
//增加一個(gè)方法,可以返回當(dāng)前棧頂?shù)闹? 但是不是真正的pop
public int peek() {
return stack[top];
}
//棧滿
public boolean isFull() {
return top == maxSize - 1;
}
//??? public boolean isEmpty() {
return top == -1;
}
//入棧-push
public void push(int value) {
//先判斷棧是否滿
if(isFull()) {
System.out.println("棧滿");
return;
}
top++;
stack[top] = value;
}
//出棧-pop, 將棧頂?shù)臄?shù)據(jù)返回
public int pop() {
//先判斷棧是否空
if(isEmpty()) {
//拋出異常
throw new RuntimeException("棧空,沒有數(shù)據(jù)~");
}
int value = stack[top];
top--;
return value;
}
//顯示棧的情況[遍歷棧], 遍歷時(shí),需要從棧頂開始顯示數(shù)據(jù)
public void list() {
if(isEmpty()) {
System.out.println("???,沒有數(shù)據(jù)~~");
return;
}
//需要從棧頂開始顯示數(shù)據(jù)
for(int i = top; i >= 0 ; i--) {
System.out.printf("stack[%d]=%d\n", i, stack[i]);
}
}
//返回運(yùn)算符的優(yōu)先級(jí),優(yōu)先級(jí)是程序員來確定, 優(yōu)先級(jí)使用數(shù)字表示
//數(shù)字越大,則優(yōu)先級(jí)就越高.
public int priority(int oper) {
if(oper == '*' || oper == '/'){
return 1;
} else if (oper == '+' || oper == '-') {
return 0;
} else {
return -1; // 假定目前的表達(dá)式只有 +, - , * , /
}
}
//判斷是不是一個(gè)運(yùn)算符
public boolean isOper(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
//計(jì)算方法
public int cal(int num1, int num2, int oper) {
int res = 0; // res 用于存放計(jì)算的結(jié)果
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;
}
}
三種表達(dá)式(前綴 中綴 后綴)
前綴表達(dá)式(波蘭表達(dá)式)
前綴表達(dá)式又稱波蘭式,前綴表達(dá)式的運(yùn)算符位于操作數(shù)之前
舉例說明: (3+4)×5-6 對(duì)應(yīng)的前綴表達(dá)式就是 - × + 3 4 5 6
前綴表達(dá)式的計(jì)算機(jī)求值
從右至左掃描表達(dá)式,遇到數(shù)字時(shí),將數(shù)字壓入堆棧,遇到運(yùn)算符時(shí),彈出棧頂?shù)膬蓚€(gè)數(shù),用運(yùn)算符對(duì)它們做相應(yīng)的計(jì)算(棧頂元素 和 次頂元素),并將結(jié)果入棧;重復(fù)上述過程直到表達(dá)式最左端,最后運(yùn)算得出的值即為表達(dá)式的結(jié)果
例如: (3+4)×5-6 對(duì)應(yīng)的前綴表達(dá)式就是 - × + 3 4 5 6 , 針對(duì)前綴表達(dá)式求值步驟如下:
從右至左掃描,將6、5、4、3壓入堆棧
遇到+運(yùn)算符,因此彈出3和4(3為棧頂元素,4為次頂元素),計(jì)算出3+4的值,得7,再將7入棧
接下來是×運(yùn)算符,因此彈出7和5,計(jì)算出7×5=35,將35入棧
最后是-運(yùn)算符,計(jì)算出35-6的值,即29,由此得出最終結(jié)果
中綴表達(dá)式
中綴表達(dá)式就是常見的運(yùn)算表達(dá)式,如(3+4)×5-6
中綴表達(dá)式的求值是我們?nèi)俗钍煜さ?,但是?duì)計(jì)算機(jī)來說卻不好操作(前面我們講的案例就能看的這個(gè)問題),因此,在計(jì)算結(jié)果時(shí),往往會(huì)將中綴表達(dá)式轉(zhuǎn)成其它表達(dá)式來操作(一般轉(zhuǎn)成后綴表達(dá)式.)
后綴表達(dá)式
后綴表達(dá)式又稱逆波蘭表達(dá)式,與前綴表達(dá)式相似,只是運(yùn)算符位于操作數(shù)之后
中舉例說明: (3+4)×5-6 對(duì)應(yīng)的后綴表達(dá)式就是 3 4 + 5 × 6 –
再比如:

后綴表達(dá)式的計(jì)算機(jī)求值
從左至右掃描表達(dá)式,遇到數(shù)字時(shí),將數(shù)字壓入堆棧,遇到運(yùn)算符時(shí),彈出棧頂?shù)膬蓚€(gè)數(shù),用運(yùn)算符對(duì)它們做相應(yīng)的計(jì)算(次頂元素 和 棧頂元素),并將結(jié)果入棧;重復(fù)上述過程直到表達(dá)式最右端,最后運(yùn)算得出的值即為表達(dá)式的結(jié)果
例如: (3+4)×5-6 對(duì)應(yīng)的后綴表達(dá)式就是 3 4 + 5 × 6 - , 針對(duì)后綴表達(dá)式求值步驟如下:
從左至右掃描,將3和4壓入堆棧;
遇到+運(yùn)算符,因此彈出4和3(4為棧頂元素,3為次頂元素),計(jì)算出3+4的值,得7,再將7入棧;
將5入棧;
接下來是×運(yùn)算符,因此彈出5和7,計(jì)算出7×5=35,將35入棧;
將6入棧;
最后是-運(yùn)算符,計(jì)算出35-6的值,即29,由此得出最終結(jié)果
完成一個(gè)逆波蘭計(jì)算器,要求完成如下任務(wù):
輸入一個(gè)逆波蘭表達(dá)式(后綴表達(dá)式),使用棧(Stack), 計(jì)算其結(jié)果
支持小括號(hào)和多位數(shù)整數(shù),因?yàn)檫@里主要講的是數(shù)據(jù)結(jié)構(gòu),因此計(jì)算器進(jìn)行簡(jiǎn)化,只支持對(duì)整數(shù)的計(jì)算。
代碼完成
package cn.icanci.datastructure.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* @Author: icanci
* @ProjectName: AlgorithmAndDataStructure
* @PackageName: cn.icanci.datastructure.stack
* @Date: Created in 2020/3/2 19:52
* @ClassAction: 逆波蘭表達(dá)式
*/
public class PolandNotation {
public static void main(String[] args) {
//先定義一個(gè)逆波蘭表達(dá)式
// (3+4)x5-6 => 3 4 + 5 * 6 -
String suffixExpresion = "38 4 + 5 * 6 - ";
//思路
//1.先將 suffixExpresion 放在 ArrayList中
//2.將ArrayList 傳遞一個(gè)方法 配合棧完成運(yùn)算
List<String> listString = getListString(suffixExpresion);
System.out.println(listString);
int val = calculate(listString);
System.out.println(val);
}
//將一個(gè)后綴 (逆波蘭) 表達(dá)式 放在一個(gè)ArrayList中
public static List<String> getListString(String suffixExpresion) {
//將 suffixExpresion 分隔
String[] s = suffixExpresion.split(" ");
List<String> list = new ArrayList<String>();
for (String ele : s) {
list.add(ele);
}
return list;
}
//完成對(duì)逆波蘭表達(dá)式的計(jì)算
public static int calculate(List<String> list) {
//創(chuàng)建棧 只需要一個(gè)棧
Stack<String> stack = new Stack<>();
//遍歷
for (String ele : list) {
//使用正則表達(dá)式
if (ele.matches("\\d+")) {
//如果匹配的是數(shù)字
//入站
stack.push(ele);
} else {
int res = 0;
//pop 數(shù)運(yùn)算
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
if (ele.equals("+")) {
res = num2 + num1;
} else if (ele.equals("-")) {
res = num1 - num2;
} else if (ele.equals("*")) {
res = num1 * num2;
} else if (ele.equals("/")) {
res = num1 / num2;
} else {
System.out.println("數(shù)據(jù)輸入錯(cuò)誤");
}
stack.push(res + "");
}
}
//返回最有一個(gè)
return Integer.parseInt(stack.pop());
}
}
中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式
后綴表達(dá)式適合計(jì)算式進(jìn)行運(yùn)算,但是人卻不太容易寫出來,尤其是表達(dá)式很長(zhǎng)的情況下,因此在開發(fā)中,我們需要將 中綴表達(dá)式轉(zhuǎn)成后綴表達(dá)式。
具體步驟如下:
初始化兩個(gè)棧:運(yùn)算符棧s1和儲(chǔ)存中間結(jié)果的棧s2;
從左至右掃描中綴表達(dá)式;
遇到操作數(shù)時(shí),將其壓s2;
遇到運(yùn)算符時(shí),比較其與s1棧頂運(yùn)算符的優(yōu)先級(jí):
如果s1為空,或棧頂運(yùn)算符為左括號(hào)“(”,則直接將此運(yùn)算符入棧;
否則,若優(yōu)先級(jí)比棧頂運(yùn)算符的高,也將運(yùn)算符壓入s1;
否則,將s1棧頂?shù)倪\(yùn)算符彈出并壓入到s2中,再次轉(zhuǎn)到(4-1)與s1中新的棧頂運(yùn)算符相比較;
中綴表達(dá)式 1 + ( ( 2 + 3 )× 4) - 5 =》 后綴表達(dá)式
將 s2 出棧 - 5 + * 4 + 3 2 1 => 1 2 3 + 4 * + 5 -
- 初始化兩個(gè)棧:運(yùn)算符棧s1和儲(chǔ)存中間結(jié)果的棧s2;
- 從左至右掃描中綴表達(dá)式;
- 遇到操作數(shù)時(shí),將其壓s2;
- 遇到運(yùn)算符時(shí),比較其與s1棧頂運(yùn)算符的優(yōu)先級(jí):
1.如果s1為空,或棧頂運(yùn)算符為左括號(hào)“(”,則直接將此運(yùn)算符入棧;
2.否則,若優(yōu)先級(jí)比棧頂運(yùn)算符的高,也將運(yùn)算符壓入s1;
3.否則,將s1棧頂?shù)倪\(yùn)算符彈出并壓入到s2中,再次轉(zhuǎn)到(4.1)與s1中新的棧頂運(yùn)算符相比較;- 遇到括號(hào)時(shí):(1) 如果是左括號(hào)“(”,則直接壓入s1 (2) 如果是右括號(hào)“)”,則依次彈出s1棧頂?shù)倪\(yùn)算符,并壓入s2,直到遇到左括號(hào)為止,此時(shí)將這一對(duì)括號(hào)丟棄
- 重復(fù)步驟2至5,直到表達(dá)式的最右邊
- 將s1中剩余的運(yùn)算符依次彈出并壓入s2
依次彈出s2中的元素并輸出,結(jié)果的逆序即為中綴表達(dá)式對(duì)應(yīng)的后綴表達(dá)式
圖解
代碼實(shí)現(xiàn)
把中綴表達(dá)式轉(zhuǎn)換為后綴表達(dá)式
public static List<String> toInfixExpressionList(String expression) {
//定義一個(gè)List
List<String> list = new ArrayList<String>();
//遍歷字符串
int i = 0;
//對(duì)多位數(shù)的拼接
String str;
//每次遍歷一個(gè)字符 就放到c中
char c;
do {
//如果是非數(shù)字
if ((c = expression.charAt(i)) < 48 || (c = expression.charAt(i)) > 57) {
list.add(String.valueOf(c));
i++;
} else {
//如果是一個(gè)數(shù) 需要考慮多位數(shù)
str = "";
while (i < expression.length() && (c = expression.charAt(i)) >= 48 && (c = expression.charAt(i)) <= 57) {
str = str + c;
i++;
}
list.add(str);
}
} while (i < expression.length());
return list;
}
public static List<String> parseList(List<String> list) {
//初始化棧
Stack<String> s1 = new Stack<>();
//因?yàn)閟2沒有pop操作 比較麻煩 此時(shí)使用 List操作
//Stack<String> s2 = new Stack<>();
List<String> s2 = new ArrayList<> ();
//遍歷list
for (String ss : list){
//如果是一個(gè)數(shù) 入棧
if (ss.matches("\\d+")){
s2.add(ss);
}else if (ss.equals("(")){
s1.push(ss);
}else if (ss.equals(")")){
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();
}else {
//當(dāng)s1棧
while (s1.size()!=0&&Operation.getValue(s1.peek()) >= Operation.getValue(ss)){
s2.add(s1.pop());
}
//還需要加入 ss 到棧
s1.push(ss);
}
}
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
完整代碼
package cn.icanci.datastructure.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
* @Author: icanci
* @ProjectName: AlgorithmAndDataStructure
* @PackageName: cn.icanci.datastructure.stack
* @Date: Created in 2020/3/2 19:52
* @ClassAction: 逆波蘭表達(dá)式
*/
public class PolandNotation {
public static void main(String[] args) {
//1.(3+4)x5-6 => 3 4 + 5 * 6 -
//2.直接對(duì)str掃描 不方便 因此需要小轉(zhuǎn)出 中綴的 list
String exptession = "1+((2+3)*4)-5";
List<String> toInfixExpr = toInfixExpressionList(exptession);
System.out.println(toInfixExpr);
List<String> suffixExpresion = parseList(toInfixExpr);
int val = calculate(suffixExpresion);
System.out.println(val);
}
//將中綴表達(dá)式占城為對(duì)應(yīng)的List
public static List<String> toInfixExpressionList(String expression) {
//定義一個(gè)List
List<String> list = new ArrayList<String>();
//遍歷字符串
int i = 0;
//對(duì)多位數(shù)的拼接
String str;
//每次遍歷一個(gè)字符 就放到c中
char c;
do {
//如果是非數(shù)字
if ((c = expression.charAt(i)) < 48 || (c = expression.charAt(i)) > 57) {
list.add(String.valueOf(c));
i++;
} else {
//如果是一個(gè)數(shù) 需要考慮多位數(shù)
str = "";
while (i < expression.length() && (c = expression.charAt(i)) >= 48 && (c = expression.charAt(i)) <= 57) {
str = str + c;
i++;
}
list.add(str);
}
} while (i < expression.length());
return list;
}
public static List<String> parseList(List<String> list) {
//初始化棧
Stack<String> s1 = new Stack<>();
//因?yàn)閟2沒有pop操作 比較麻煩 此時(shí)使用 List操作
//Stack<String> s2 = new Stack<>();
List<String> s2 = new ArrayList<> ();
//遍歷list
for (String ss : list){
//如果是一個(gè)數(shù) 入棧
if (ss.matches("\\d+")){
s2.add(ss);
}else if (ss.equals("(")){
s1.push(ss);
}else if (ss.equals(")")){
while (!s1.peek().equals("(")){
s2.add(s1.pop());
}
s1.pop();
}else {
//當(dāng)s1棧
while (s1.size()!=0&&Operation.getValue(s1.peek()) >= Operation.getValue(ss)){
s2.add(s1.pop());
}
//還需要加入 ss 到棧
s1.push(ss);
}
}
while (s1.size()!=0){
s2.add(s1.pop());
}
return s2;
}
//將一個(gè)后綴 (逆波蘭) 表達(dá)式 放在一個(gè)ArrayList中
public static List<String> getListString(String suffixExpresion) {
//將 suffixExpresion 分隔
String[] s = suffixExpresion.split(" ");
List<String> list = new ArrayList<String>();
for (String ele : s) {
list.add(ele);
}
return list;
}
//完成對(duì)逆波蘭表達(dá)式的計(jì)算
public static int calculate(List<String> list) {
//創(chuàng)建棧 只需要一個(gè)棧
Stack<String> stack = new Stack<>();
//遍歷
for (String ele : list) {
//使用正則表達(dá)式
if (ele.matches("\\d+")) {
//如果匹配的是數(shù)字
//入站
stack.push(ele);
} else {
int res = 0;
//pop 數(shù)運(yùn)算
int num2 = Integer.parseInt(stack.pop());
int num1 = Integer.parseInt(stack.pop());
if (ele.equals("+")) {
res = num2 + num1;
} else if (ele.equals("-")) {
res = num1 - num2;
} else if (ele.equals("*")) {
res = num1 * num2;
} else if (ele.equals("/")) {
res = num1 / num2;
} else {
System.out.println("數(shù)據(jù)輸入錯(cuò)誤");
}
stack.push(res + "");
}
}
//返回最有一個(gè)
return Integer.parseInt(stack.pop());
}
}
//編寫一個(gè)類
class Operation{
private static int ADD = 1;
private static int SUB = 1;
private static int MUL = 2;
private static int DIV = 2;
public static int getValue(String operation){
int result = 0;
switch (operation){
case "+":
result = ADD;
break;
case "-":
result = SUB;
break;
case "*":
result = MUL;
break;
case "/":
result = DIV;
break;
default:
System.out.println("找不到的元算符");
break;
}
return result;
}
}
逆波蘭表達(dá)式計(jì)算器完整班
完整版的逆波蘭計(jì)算器,功能包括
支持 + - * / ( )
多位數(shù),支持小數(shù),
兼容處理, 過濾任何空白字符,包括空格、制表符、換頁符
逆波蘭計(jì)算器完整版考慮的因素較多,下面給出完整版代碼供同學(xué)們學(xué)習(xí),其基本思路和前面一樣,也是使用到:中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式。
代碼實(shí)現(xiàn)
package cn.icanci.datastructure.stack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.regex.Pattern;
/**
* @Author: icanci
* @ProjectName: AlgorithmAndDataStructure
* @PackageName: cn.icanci.datastructure.stack
* @Date: Created in 2020/3/2 21:55
* @ClassAction:
*/
public class ReversePolishMultiCalc {
/**
* 匹配 + - * / ( ) 運(yùn)算符
*/
static final String SYMBOL = "\\+|-|\\*|/|\\(|\\)";
static final String LEFT = "(";
static final String RIGHT = ")";
static final String ADD = "+";
static final String MINUS= "-";
static final String TIMES = "*";
static final String DIVISION = "/";
/**
* 加減 + -
*/
static final int LEVEL_01 = 1;
/**
* 乘除 * /
*/
static final int LEVEL_02 = 2;
/**
* 括號(hào)
*/
static final int LEVEL_HIGH = Integer.MAX_VALUE;
static Stack<String> stack = new Stack<>();
static List<String> data = Collections.synchronizedList(new ArrayList<String>());
/**
* 去除所有空白符
* @param s
* @return
*/
public static String replaceAllBlank(String s ){
// \\s+ 匹配任何空白字符,包括空格、制表符、換頁符等等, 等價(jià)于[ \f\n\r\t\v]
return s.replaceAll("\\s+","");
}
/**
* 判斷是不是數(shù)字 int double long float
* @param s
* @return
*/
public static boolean isNumber(String s){
Pattern pattern = Pattern.compile("^[-\\+]?[.\\d]*$");
return pattern.matcher(s).matches();
}
/**
* 判斷是不是運(yùn)算符
* @param s
* @return
*/
public static boolean isSymbol(String s){
return s.matches(SYMBOL);
}
/**
* 匹配運(yùn)算等級(jí)
* @param s
* @return
*/
public static int calcLevel(String s){
if("+".equals(s) || "-".equals(s)){
return LEVEL_01;
} else if("*".equals(s) || "/".equals(s)){
return LEVEL_02;
}
return LEVEL_HIGH;
}
/**
* 匹配
* @param s
* @throws Exception
*/
public static List<String> doMatch (String s) throws Exception{
if(s == null || "".equals(s.trim())) throw new RuntimeException("data is empty");
if(!isNumber(s.charAt(0)+"")) throw new RuntimeException("data illeagle,start not with a number");
s = replaceAllBlank(s);
String each;
int start = 0;
for (int i = 0; i < s.length(); i++) {
if(isSymbol(s.charAt(i)+"")){
each = s.charAt(i)+"";
//棧為空,(操作符,或者 操作符優(yōu)先級(jí)大于棧頂優(yōu)先級(jí) && 操作符優(yōu)先級(jí)不是( )的優(yōu)先級(jí) 及是 ) 不能直接入棧
if(stack.isEmpty() || LEFT.equals(each)
|| ((calcLevel(each) > calcLevel(stack.peek())) && calcLevel(each) < LEVEL_HIGH)){
stack.push(each);
}else if( !stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek())){
//棧非空,操作符優(yōu)先級(jí)小于等于棧頂優(yōu)先級(jí)時(shí)出棧入列,直到棧為空,或者遇到了(,最后操作符入棧
while (!stack.isEmpty() && calcLevel(each) <= calcLevel(stack.peek()) ){
if(calcLevel(stack.peek()) == LEVEL_HIGH){
break;
}
data.add(stack.pop());
}
stack.push(each);
}else if(RIGHT.equals(each)){
// ) 操作符,依次出棧入列直到空棧或者遇到了第一個(gè))操作符,此時(shí))出棧
while (!stack.isEmpty() && LEVEL_HIGH >= calcLevel(stack.peek())){
if(LEVEL_HIGH == calcLevel(stack.peek())){
stack.pop();
break;
}
data.add(stack.pop());
}
}
start = i ; //前一個(gè)運(yùn)算符的位置
}else if( i == s.length()-1 || isSymbol(s.charAt(i+1)+"") ){
each = start == 0 ? s.substring(start,i+1) : s.substring(start+1,i+1);
if(isNumber(each)) {
data.add(each);
continue;
}
throw new RuntimeException("data not match number");
}
}
//如果棧里還有元素,此時(shí)元素需要依次出棧入列,可以想象棧里剩下棧頂為/,棧底為+,應(yīng)該依次出棧入列,可以直接翻轉(zhuǎn)整個(gè)stack 添加到隊(duì)列
Collections.reverse(stack);
data.addAll(new ArrayList<>(stack));
System.out.println(data);
return data;
}
/**
* 算出結(jié)果
* @param list
* @return
*/
public static Double doCalc(List<String> list){
Double d = 0d;
if(list == null || list.isEmpty()){
return null;
}
if (list.size() == 1){
System.out.println(list);
d = Double.valueOf(list.get(0));
return d;
}
ArrayList<String> list1 = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
list1.add(list.get(i));
if(isSymbol(list.get(i))){
Double d1 = doTheMath(list.get(i - 2), list.get(i - 1), list.get(i));
list1.remove(i);
list1.remove(i-1);
list1.set(i-2,d1+"");
list1.addAll(list.subList(i+1,list.size()));
break;
}
}
doCalc(list1);
return d;
}
/**
* 運(yùn)算
* @param s1
* @param s2
* @param symbol
* @return
*/
public static Double doTheMath(String s1,String s2,String symbol){
Double result ;
switch (symbol){
case ADD : result = Double.valueOf(s1) + Double.valueOf(s2); break;
case MINUS : result = Double.valueOf(s1) - Double.valueOf(s2); break;
case TIMES : result = Double.valueOf(s1) * Double.valueOf(s2); break;
case DIVISION : result = Double.valueOf(s1) / Double.valueOf(s2); break;
default : result = null;
}
return result;
}
public static void main(String[] args) {
//String math = "9+(3-1)*3+10/2";
String math = "12.8 + (2 - 3.55)*4+10/5.0";
try {
doCalc(doMatch(math));
} catch (Exception e) {
e.printStackTrace();
}
}
}




