生活随笔
收集整理的這篇文章主要介紹了
数据结构与算法--简单栈实现及其应用
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
棧
- 棧(Stack)是一種限制插入和刪除只能在一個位置上進行的表,改位置是表的末端,叫做棧頂top。棧的基本操作有push (進棧)pop(出棧)
- 棧又叫做LIFO(后進先出)表,下圖展示普通push輸入,而pop和top是輸出操作。普通的清空棧,判斷是否空棧操作都是基本操作指令,但是我們對棧的具體修改操作基本也就是push和pop。下圖模型中表示若干操作后,一個抽象的棧只有棧頂元素是可見的,其他不可見。
棧的實現
- 由于棧是一個表,因此任何實現表的方法都能夠實現棧,顯然,ArrayList, LinkedList都支持這些操作,99%的時間他們都是最合理的選擇,偶爾設計一種特殊目的的實現可能會更快。因為棧操作是常數時間操作,所以除非在非常特殊的環境下,一般都是這兩種方式,這兩種一種是鏈式結構,一種是數組結構,二者都簡化了在ArrayList和LinkedList中的邏輯,
棧的鏈表實現
- 棧的第一種實現是單鏈表,通過在表的頂端插入實現push,通過刪除表頂端實現pop。top操作只是查詢表頂端元素并返回他的值。
棧的數組實現
- 另一種實現方法避免了鏈而且可能是最流行的解決方法。模擬ArrayList的Add操作,因此現有的實現方法非常簡單,與每個棧相關聯的參是push,pop,都是對數組末尾的數據進行操作,對空棧他是-1。
- 將元素X推入棧,top 增加1,并且array[last]=X
- 將元素彈出棧,返回array[top],并且top減1
- 以上測操作都是以常數時間運行,非??焖俚闹挥幸粋€操作,以下我們用數組實現自己的一個棧:
public class MyStack<E> {private Object
[] elementData
;private Integer top
;private Integer capacity
;private Integer position
;public MyStack() {elementData
= new Object[10];top
= -1;capacity
= elementData
.length
- 1;position
= 0;}public void reSize() {if (position
== capacity
) {Integer newCapacity
= capacity
+ (capacity
>> 1);elementData
= Arrays
.copyOf(elementData
, newCapacity
);capacity
= newCapacity
;}}public Object
push(E e
) {reSize();top
= position
;elementData
[position
++] = e
;return e
;}public E
pop(){if(top
== -1){return null
;}E e
= (E
)elementData
[top
--];position
--;return e
;}public E
getTop(){return (E
)elementData
[top
];}public boolean isEmpty(){return top
== -1;}public int size(){return position
;}public void printMyStack(){Integer temp
= top
;while (top
!= -1){System
.out
.print(pop());}top
= temp
;}public static void main(String
[] args
) {MyStack
<Integer> myStack
= new MyStack<Integer>();for (int i
= 0; i
< 100; i
++) {myStack
.push(i
);}Integer result
= 0;while (result
!= null
){result
= myStack
.pop();System
.out
.println(result
);}}
}
應用
- 我們以上示例中將操作限制在一個表上進行,你們這些操作會執行很快,然而這樣操作是非常重要的,維護了棧中的數據關系。在棧的應用中給出了三個例子,實現了其中一個作為參考。
平衡符號
- 編譯器檢查程序的語法錯誤,經常會因為缺少一個符號,比如花括號,引起編譯器上百行的錯誤提示,而真正的錯誤并沒有找出來。
- 這種情況,有一個有用的工具就是檢驗是否每一件事情都成對出現。
- 每一個右括號,右方括號,右花括號都比如對應左邊括號。序列{()} 是合法的,但是{(]}是非法的。
- 事實上檢測這些事情是很容易的,為簡單起見,我們直接利用棧就可以做一個簡單實現,流程如下:
- 做一個空棧,讀入字符,直到文件結尾。
- 如果字符是一個開放括號,推入棧
- 如果字符是一個封閉括號,則當棧空時候,報錯,否則將棧元素彈出。
- 如果彈出的符合不是對應的開放符號的括號,報錯
- 文件結尾,如果棧非空,報錯
后綴表達式
- 假設我們有一個便攜計算器用于計算,為此,將一系列數據進行±*/之后得到結果,如果購物單價是4.99,5.99,6.99,數量是1,1,2,那么輸入這些數據的如下:
4.99 + 5.99 + 6.99 * 2
- 普通計算器并不能識別先加減后乘除,得到的結果是:35.94,但是我們期望得到的結果是 24.96
- 只有科學計算器才支持復雜計算,科學計算器,實現中,將6.99*2 結果存儲A1,將5.99與A1 相加得到A2,再將4.99與A2 相加得到組中的結果,我們將這種操作順序書寫如下
4.99 5.99 + 6.99 2 * +
- 以上操作符記錄方式叫做后綴(postfix)或者逆波蘭(reverse polish)表達式,求值過程恰好就是上面描述的過程,計算這個問題最容易的方法就是使用棧。當看到一個數時候,將他推入棧,遇到運算符就將棧中頭兩個數據彈出,計算,接入推入棧中。例如如下后綴表達式計算過程動圖如下:
- 表達式:1 2+3-4 5*+
- 如上動圖展示全流程,計算一個后綴表達式花費的世界是O(N),因為對于輸入中的每個元素的處理都是有一些棧操作組成從而花費常數的時間。算法簡單。注意:當一個表達式以后綴的方式給出的時候,沒有必要知道任何的優先規則,因為在中序,轉后綴表達式的時候已經考慮過,這個是一個明顯的優點。
中綴表達式轉后綴表達式
- 上面講到后綴表達式的計算,那我們如何將中綴表達式轉后綴,也是可以用棧來完成這個操作,我們用簡單的±*/(,這幾個操作符,我們用如下案例來說明:
a
+b
*c
+(d
*e
+f
)*g
1 2 3 * 4 5 * 6 + 7 * + +
算法分析
- 操作符優先級,‘)’右括號 > */ 乘除 > ±加減 > ‘(’ 左括號
- 依次讀取操作數,將數字推入number 棧,將操作符推入action棧
- 右括號‘)’,依次將action中操作符彈出,直到遇見左括號‘(’
- 如果見到任何其他符號(+,-,*,/,(),如果當前棧頂元素X優先級高于當前元素Y,那么彈出棧頂元素X,將X加入number棧,并將當前元素Y加入到action棧
- 如果讀到末尾,我們需要將action棧元素依次彈出加入到number棧
動圖展示
- 與前面的計算相同,這種轉換同樣也只要O(N)時間,并經過一次輸入后就完成工作。以下代碼通過自己實現的棧,實現中綴表達式轉后綴表達式,并且計算后綴表達式得到對應結果
public class Evaluate {public static MyStack
<String> middlePreTOAfterPre(String mathStr
) {MyStack
<String> number
= new MyStack<>();MyStack
<String> action
= new MyStack<>();String
[] chars
= mathStr
.split(" ");for (int i
= 0; i
< chars
.length
; i
++) {String s
= String
.valueOf(chars
[i
]);switch (flag(s
, action
)) {case 1:number
.push(s
);break;case 2:action
.push(s
);break;case 3:action
.push(s
);break;case 4:number
.push(action
.pop());action
.push(s
);break;case 5:action
.push(s
);break;case 6:while (!action
.isEmpty()) {String temp
= action
.pop();if (temp
.matches("\\(")) {break;} else {number
.push(temp
);}}break;default:break;}}while (!action
.isEmpty()) {number
.push(action
.pop());}MyStack
<String> temp
= new MyStack<>();while (!number
.isEmpty()) {temp
.push(number
.pop());}return temp
;}public static int flag(String s
, MyStack
<String> action
) {if (s
.matches("([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9])")) {return 1;}if (s
.matches("(\\*)|(\\/)|(\\+)|(\\-)")) {if (action
.isEmpty()) {return 2;} else if (prior(s
, action
.getTop())) {return 3;} else {return 4;}}if (s
.matches("\\(")) {return 5;}if (s
.matches("\\)")) {return 6;}return 0;}public static boolean prior(String s
, String top
) {if (top
.matches("\\(")) {return true;}if (s
.matches("(\\*)|(\\/)") && top
.matches("(\\+)|(\\-)")) {return true;}return false;}public static Double
evalutePostfix(MyStack
<String> myStack
){MyStack
<String> newStack
= new MyStack<>();while (!myStack
.isEmpty()){String temp
= myStack
.pop();if(temp
.matches("\\d+")){newStack
.push(temp
);}else if(temp
.matches("(\\*)|(\\/)|(\\+)|(\\-)")){if(newStack
.size() < 2){break;}double a
= Double
.valueOf(newStack
.pop());double b
= Double
.valueOf(newStack
.pop());switch (temp
){case "+":newStack
.push(String
.valueOf(b
+a
));break;case "-":newStack
.push(String
.valueOf(b
-a
));break;case "*":newStack
.push(String
.valueOf(b
*a
));break;case "/":newStack
.push(String
.valueOf(b
/a
));break;default:break;}}}return Double
.valueOf(newStack
.pop());}public static void main(String
[] args
) {MyStack
<String> middlerStack
= middlePreTOAfterPre("67 - 8 + 9 * ( 1 + 4 - 5 / 3 ) + ( 8 - 4 * 2 ) + 8 - 21");middlerStack
.printMyStack();System
.out
.println();System
.out
.println(evalutePostfix(middlerStack
));}
}
- 注意:以上帶有減法,除法, 在具體計算時候,應該是 (第二個彈出值- 第一個彈出值),除法也是類似,只有這個順序區別,其他的和以上描述一致。
- 其實中綴轉后綴唯一的一個重要規則就是:高優先級操作符入棧,需要保證低下的操作符優先級比我更低或相等,否則就彈出頂部操作符,也就是算法分析中的第四點:
- 如果見到任何其他符號(+,-,*,/,(),如果當前棧頂元素X優先級高于當前元素Y,那么彈出棧頂元素X,將X加入number棧,并將當前元素Y加入到action棧
應用三
上一篇:數據結構與算法–排序算法總結(動圖演示)
下一篇:數據結構與算法–利用棧實現隊列
總結
以上是生活随笔為你收集整理的数据结构与算法--简单栈实现及其应用的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。