假設咱們要求輸入相似這樣一個表達式:9+(3-1)*3+10/2,輸出結果。咱們知道先括號,再乘除,最後加減,中學時候使用的科學計算器,是容許輸入這樣的表達式計算結果的,那麼計算機怎麼知道這個串裏面先算括號再算乘除呢?咱們先來介紹下棧這種數據結構,再來解決這個問題。數組
前面已經說過數組的連表,如今來講另一種線性表的數據結構---棧。瀏覽器
舉個比較形象的例子,洗盤子的時候,是否是一個一個往上面堆着放,拿的時候也從上面一個一個的拿,最早放的在最下面,最後放的在最上面,拿的時候第一個拿到。這就是典型的棧結構。先進後出First In Last Out(FILO).數據結構
怎麼來實現一個棧結構呢,棧也是一種線性表,前面也有提到兩種很基礎的線性表結構的數據結構數組和鏈表。棧其實就是第一個特殊的鏈表或者數組。能夠基於數組或者鏈表來實現,成爲數組棧或者鏈棧,與之具備數組和鏈表相關特色。this
棧的特殊點在於先進去的元素放在棧低,後進的在棧頂。向棧中插入一個元素叫入棧、進棧、壓棧都行,插入的數據會被放在棧頂。從棧中取出一個元素叫出棧、退棧都行,取出以後,本來棧頂的這個元素就會被刪掉,讓它下面的那個元素成爲新的棧頂元素。google
數組棧通常棧低是索引開始的元素,壓棧就往索引增加方向走;鏈棧通常棧低是頭結點,棧頂是尾結點。spa
既然都是用數組或鏈表來實現,爲何還單獨拎出來一個數據結構呢。數組和鏈表暴露了太多了的操做。就會更容易出錯。針對性的封裝出來的棧這種結構,在某些場景會更加適合。想象一下咱們瀏覽器的的前進後退,是否是就很像兩個棧的數據在互相交換操做,一個前進棧,一個後退棧。點後退,把後退棧的棧頂彈出,放進前進棧的棧頂;再點前進,是否是就是壓進前進棧頂的後退棧的棧頂元素。就這樣互相交替着。code
想象一個程序的調用流程是否是也是一個棧結構。最後調用的方法最早執行。blog
Java裏面的Stack也是基於數組實現的,它繼承了Vector。咱們用數組實現一個簡單棧的基本操做:繼承
package com.nijunyang.algorithm.stack; /** * Description: * Created by nijunyang on 2020/4/1 23:48 */ public class MyStack<E> { private static final int DEFAULT_SIZE = 10; private Object[] elements; private int size; public MyStack() { this(DEFAULT_SIZE); } public MyStack(int capacity) { this.elements = new Object[capacity]; } /** * 入棧 * @param e */ public void push(E e) { //彈性伸縮,擴容/收縮釋放內存空間 if (size >= elements.length) { resize(size * 2); } else if (size > 0 && size < elements.length / 2) { resize(elements.length / 2); } elements[size++] = e; } /** * 出棧 */ public E pop() { if (isEmpty()) { return null; } E e = (E) elements[--size]; //size是5,那麼最後一個元素就是4也就是--size elements[size] = null; //如今size已是4了,彈出就是4這個元素的位置置爲空 return e; } public boolean isEmpty() { return size == 0; } public int size() { return size; } /** * 擴容/收縮 */ private void resize(int newCapacity) { Object[] temp = new Object[newCapacity]; for(int i = 0 ; i < size; i ++){ temp[i] = elements[i]; } elements = temp; } public static void main(String[] args){ MyStack<Integer> myStack = new MyStack(5); myStack.push(1); myStack.push(2); myStack.push(3); myStack.push(4); myStack.push(5); myStack.push(6); System.out.println(myStack.size); Integer e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); e = myStack.pop(); System.out.println(e); } }
如今用咱們看看怎麼用棧來解決9+(3-1)*3+10/2這個計算問題索引
首先咱們要怎麼來處理括號和運算符號的優先級呢
這裏先說一下中綴表達式和後綴表達式,像這個表達式9+(3-1)*3+10/2就是中綴表達式,若是咱們轉換成9 3 1 - 3 * + 10 2 / + 這個就是後綴表達式,後綴表達式也叫逆波蘭,能夠能夠自行百度或者google,後綴表達式就是操做符號在兩個操做數的後面,而中綴表達式就是操做符號在兩個操做數的中間。
看下後綴表達式是怎麼操做的,就是遇到操做符號就把前面兩個數進行符號運算:
9 3 1 - 3 * + 10 2 / + 這個表達式的操做以下:
9 3 1 - 這個時候就把3和1 相減獲得2 => 9 2 3 * 這個時候就把2和3相乘 獲得6 =>
9 6 + => 15 10 2 / =>15 5 + => 20
大體就是這麼個流程,這個過程是否是很像棧的操做,遇到數字就入棧,遇到符號就把數字前面兩個數字出棧進行計算,而後將結果入棧,直到表達式結束。
如今咱們只要把中綴表達式轉換成後綴表達式就能夠進行計算了。看下百度的轉換流程
簡單來講就是用一個棧來存放符號,而後從左到右遍歷中綴表達式的數字和字符,如果數字就輸出,如果符號則判斷和棧頂符號的優先級,若是是括號或優先級低於棧頂元素,則依次出棧並輸出,將當前符號進棧,直到最後結束。
9+(3-1)*3+10/2
先初始化一個棧stack,而後依次遍歷咱們的中綴表達式,操做邏輯以下:
從上述邏輯中能夠看到,無論是最後的計算,仍是中綴表達式轉後綴表達式中都用到棧這種數據結構。