2021/9/17(棧實現+中字尾表示式求值)
2021/9/17(棧實現+表示式求值)
棧的應用場景
- 子程式的呼叫,在跳往子程式之前,會講下個指令的地址存到堆疊中,直到子程式執行完後再將地址取出,以便回到原來的程式。
- 處理遞迴呼叫:和子程式的呼叫類似,只是除了儲存下一指令的地址外,也將引數,區域變數等資料存入堆疊中。
- 表示式的轉換【中綴轉字尾】與求值(實際解決)。
- 二叉樹的遍歷。
- 圖形的深度優先(depth-first)搜尋法。
參考了 https://www.cnblogs.com/hapjin/p/4442729.html
1 、使用JAVA陣列實現順序棧、
1、首先總結一下線性表(分為順序表和連結表,【即順序儲存結構和鏈式儲存結構的區別】)和棧(順序棧和連結棧)還有佇列(順序佇列和連結佇列)的JAVA類庫中的實現:
java.util.ArrayList 實現了順序表,java.util.LinkedList 實現了連結表的功能。
java.util.ArrayDeque實現了順序棧和順序佇列(該類中即定義了與棧操作有關的方法,也定義了與佇列操作有關的方法)、java.util.LinkedList實現了連結棧和連結佇列。
2、定義了一個Stack
建立一個基於陣列的Stack介面:
public interface Stack<E> { public int length();//返回棧的長度 public E pop();//出棧 public void push(E element);//進棧 public E peek();//訪問棧頂元素 public boolean empty();//判斷棧是否為空 public void clear();//清空棧 }
3,在JAVA類庫中,java.util.ArrayDeque類實現了順序棧的功能。ArrayDeque可以實現動態地擴充套件棧的大小,但是不支援多執行緒訪問。同時,ArrayDeque還實現了順序佇列的功能。
4,定義了一個Object[] 型別的陣列,用來儲存順序棧中的元素。具體實現類MyArrayListStack 如下:
package stack; import java.util.Arrays; public class MyArrayListStack<T> implements Stack<T>{ private int DEFAULT_SIZE=16; //定義棧的初始預設長度 private int capacity; // 儲存棧的長度 private int size; //元素個數 private Object[] elementData; //儲存棧中的元素 public int getCapacity() { return capacity; } public void setCapacity(int capacity) { this.capacity = capacity; } public MyArrayListStack() { capacity = DEFAULT_SIZE; elementData = new Object[capacity]; size = 0; } // 以指定長度的大小來建立棧 public MyArrayListStack(int initSize) { capacity = 1; while (capacity<initSize) capacity <<=1; //將capacity設定成大於initSize的最小2次方 elementData = new Object[capacity]; } @Override public int getLength() { return this.size; } public T getAt(int idx){ return elementData(idx); } @Override public T pop() { if (empty()){ throw new IndexOutOfBoundsException("棧空,不能出棧"); } T oldValue =elementData(size-1); elementData[--size] = null; ////讓垃圾回收器及時回收,避免記憶體洩露 return oldValue; } /** * 返回運算子的優先順序,數字越大優先順序就越高 * 目前 表示式只有+ - * / */ public int priority(int oper){ if (oper == '*' || oper =='/'){ return 1; }else if(oper =='+' || oper =='-'){ return 0; }else return -1; } public boolean isOper(char val){ return val == '+' || val =='-' || val=='*' ||val =='/'; } public int cal(int num1,int num2,char 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; } /** * 我用得有一點雞肋了 ,但ArrayList中有這麼用過 */ T elementData(int index){ return (T) elementData[index]; } /** * 動態擴容 真的太牛逼了! */ public void ensureCapacity(int minCapacity){ if (minCapacity>capacity){ while(capacity<minCapacity) capacity <<=1; } elementData = Arrays.copyOf(elementData,capacity); } @Override public void push(T t) { ensureCapacity(size+1); elementData[size++] = t; } @Override public T peek() { if (size==0){ throw new ArrayIndexOutOfBoundsException("size = 0"); } return elementData(size-1); } @Override public boolean empty() { return size==0; } public boolean isNotEmpty(){ return !empty(); } @Override public void clear() { for (int i = 0; i < size; i++) { elementData[i] = null; } size = 0; } public void display() { if (size==0){ System.out.println("[]"); } StringBuilder sb = new StringBuilder(""); for (int j =size-1;j>=0;j--){ sb.append(elementData[j].toString()+", "); } int len = sb.length(); System.out.println(sb.delete(len - 2, len).append(']').toString()); }
使用單鏈表實現棧
package stack;
public class MyLinkedListStack<E> implements Stack<E>{
private int size;
private Node<E> first;
private Node<E> rear;
static class Node<E>{
E item;
Node<E> next;
Node(E x){
item = x;
}
}
public E getFirst() {
return first.item;
}
public E getRear() {
return rear.item;
}
@Override
public int getLength() {
return size;
}
@Override
public E pop() {
if (size==1){
E val = rear.item;
first=null;
rear=null;
size--;
return val;
}
Node<E> preNode = first;
E val = rear.item;
while (preNode.next!=rear){
preNode = preNode.next;
}
preNode.next = null;
rear = preNode;
size--;
return val;
}
@Override
public void push(E e) {
Node<E> node = new Node(e);
if (size==0){
first = node;
rear = node;
size++;
return;
}
rear.next = node;
rear = node;
size++;
}
@Override
public E peek() {
return rear.item;
}
@Override
public boolean empty() {
return size==0;
}
@Override
public void clear() {
while (first!=null){
pop();
}
}
public void display(){
Node<E> tmp = first;
for (int i = 0; i < size; i++) {
System.out.println(tmp.item);
tmp = tmp.next;
}
}
}
3、使用棧完成表示式的計算思路
1、通過一個遍歷,來遍歷我們的表示式
2、如果是數字,入數字棧
3、符號入符號棧
3.1、如果符號棧為空,直接入
3.2、如果符號棧有操作符,就進行比較,如果操作符號優先順序<=棧中的操作符,
需要從數棧中pop出二個數,再從符號棧pop出一個符號,進行運算。得到結果入
數字棧,再入符號棧,如果當前的操作符的優先順序大於棧中的操作符,入符號棧。
4、表示式掃描完畢,就順序的從數棧和符號棧pop出相應的數和符號,運算。
5、最後在數棧只有一個數字即結果。
只能計算簡單 ,解決了 5-3-1 問題
package stack;
public class CalStack {
public static void main(String[] args) {
String str = "5-5/5-1"; //中綴表示式
// 建立二個棧,數棧,一個符號棧
MyArrayListStack<Integer> numStack = new MyArrayListStack<>();
MyArrayListStack<Integer> operStack = new MyArrayListStack<>();
int index = 0; //遍歷
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' '; // 將每次掃描得到的ch儲存到
String keepNum = ""; // 儲存長的數字
while (true){
// 依次得到str的每一個位元組
ch = str.charAt(index);
if(operStack.isOper(ch)){
// 判斷當前符號棧是不是為空
if (operStack.isNotEmpty()){
if (operStack.priority(ch)<=operStack.priority(operStack.peek())){
if (ch=='-'&& operStack.peek()=='-'){
ch = '+';
}
num1 = numStack.pop();
num2 = numStack.pop();
oper = operStack.pop();
res = numStack.cal(num1,num2, (char) oper);
numStack.push(res);
operStack.push((int) ch);
}else {
operStack.push((int) ch);
}
}else{
operStack.push((int) ch);
}
}else {
keepNum+=ch;
// 如果ch已經是str的最後一位,就直接入棧
if (index==str.length()-1){
numStack.push(Integer.parseInt(keepNum));
}else {
if(operStack.isOper(str.charAt(index+1))){
// 後一位是操作符
numStack.push(Integer.parseInt(keepNum));
keepNum = "";
}
}
//numStack.push(Integer.parseInt(String.valueOf(ch)));
}
index+=1;
if (index==str.length()){
break;
}
}
while (true){
if (operStack.empty()){
break;
}
num1 = numStack.pop();
num2 = numStack.pop();
System.out.println(operStack.getLength());
oper = operStack.pop();
if (oper=='-' && operStack.getLength()!=0 && operStack.peek()=='-') {
oper ='+';
}
res = numStack.cal(num1,num2, (char) oper);
numStack.push(res);
}
System.out.println("表示式為:"+numStack.pop());
}
}
10進位制轉換n進位制:
public void conversion10_n(int num,int toNum){
MyArrayListStack<Integer> stack = new MyArrayListStack<>();
while (num!=0){
stack.push(num%toNum);
num = num/8;
}
while (stack.isNotEmpty()){
System.out.printf("%d",stack.pop());
}
}
3、字首(波蘭表示式),中綴(我們使用的),字尾表示式(逆波蘭表示式)
x綴說的是符號的位置,符號在前 字首
中綴如何轉字首字尾呢?
a+b*c-(d+e)
第一步:按照運算子的優先順序對所有的運算單位加括號
式子變成拉:((a+(bc))-(d+e))
第二步:轉換字首與字尾表示式
字首:把運算子號移動到對應的括號前面
則變成拉:-( +(a (bc)) +(de))
把括號去掉:-+abc+de 字首式子出現
字尾:把運算子號移動到對應的括號後面
則變成拉:((a(bc) )- (de)+ )-改成:((a(bc)* )+ (de)+ )-
把括號去掉:abc-de+- 字尾式子出現改成:abc+de+-
發現沒有,字首式,字尾式是不需要用括號來進行優先順序的確定的。
字尾表示式求解簡單式子
逆波蘭表示式 很適合計算機運算。
從左向右 遇到第一個字元,從棧(這次不分數棧還是符號棧)中pop二個進行計算。結果push進去
polandExpression("3 4 + 5 * 6 -");
public static void polandExpression(String suffixExpression){
String[] split = suffixExpression.split(" ");
List<String> list = Arrays.asList(split);
MyArrayListStack<String> stack = new MyArrayListStack<>();
for (String s : list) {
if (s.matches("\\d")){
stack.push(s);
}else {
int num1 = Integer.parseInt(stack.pop());
int num2 = Integer.parseInt(stack.pop());
int res = 0;
try{
res = stack.cal(num1,num2,s.charAt(0));
}catch (Exception e){
e.printStackTrace();
}
stack.push("" + res);
}
}
System.out.println("value: "+stack.pop());
}