咱們能夠用棧實現瀏覽器的前進後退功能
那麼棧又是什麼?
棧就像一疊盤子,從下一個一個往上放。
後進先出,先進後出,就是典型棧的結構。
從操做特性上來看,棧是十分受限制的一種數據結構,只有一端可以操做,但由於只暴露了一端的操做接口,便不容易出錯,更可控。
所以當數據集合知足先進後出,後進先出的特色時,就應該選擇棧。
順序棧:用數組實現的棧
鏈式棧:用鏈表實現的棧
Java實現的順序棧:
1 // 基於數組實現的順序棧
2 public class ArrayStack {
3 private String[] items; // 數組
4 private int count; // 棧中元素個數
5 private int n; // 棧的大小
6
7 // 初始化數組,申請一個大小爲 n 的數組空間
8 public ArrayStack(int n) {
9 this.items = new String[n];
10 this.n = n;
11 this.count = 0;
12 }
13
14 // 入棧操做
15 public boolean push(String item) {
16 // 數組空間不夠了,直接返回 false,入棧失敗。
17 if (count == n) return false;
18 // 將 item 放到下標爲 count 的位置,而且 count 加一
19 items[count] = item;
20 ++count;
21 return true;
22 }
23
24 // 出棧操做
25 public String pop() {
26 // 棧爲空,則直接返回 null
27 if (count == 0) return null;
28 // 返回下標爲 count-1 的數組元素,而且棧中元素個數 count 減一
29 String tmp = items[count-1];
30 --count;
31 return tmp;
32 }
33 }
無論順序棧仍是鏈表棧,存儲數據是隻須要一個大小爲n的數組就夠了,而出棧入棧的操做,只須要一兩個臨時變量空間,所以
空間複雜度爲O(1)。
這裏要注意,
空間複雜度並非指 本來存儲數據的空間,而是指算法執行時還須要額外的存儲空間。
出棧入棧的時間複雜度均爲O(1)。
支持動態擴容的順序棧
前面講的數組若要實現動態擴容即是將數組數據複製到更大的內存去,對於動態擴容的順序棧,咱們只須要底層是一個動態擴容的數組。
對於動態擴容的數組平時開發用到很少,但咱們主要對其進行復雜度分析。
它出棧複雜度都是O(1)。
對於入棧(push),最好狀況時間複雜度是O(1),最壞時間複雜度是O(n)。那麼它的均攤時間複雜度是多少呢?
便於分析假設
- 棧空間不夠時,從新申請原來兩倍大小的空間。
- 沒有出棧操做
- 不涉及內存搬移的入棧操做爲simple_push,時間複雜度爲O(1)。
假設當前棧大小爲K,若棧已滿,push須要申請兩倍內存,並進行K個數據搬移,再進行push,操做複雜度爲O(K)。但有2K的空間後,接下來K-1次push都爲simple-push,時間複雜度爲O(1)。若是還有push,再次循環下去。
講須要數據搬移的push均攤下到k-1次的simple的入棧,即可知push的均攤時間複雜度爲O(1)。
棧在函數調用的應用
操做系統給每一個線程分配了一塊獨立的內存空間,這個內存會被組織成」棧」,用來存放臨時變量。
1 int main() {
2 int a = 1;
3 int ret = 0;
4 int res = 0;
5 ret = add(3, 5);
6 res = a + ret;
7 printf("%d", res);
8 reuturn 0;
9 }
10
11 int add(int x, int y) {
12 int sum = 0;
13 sum = x + y;
14 return sum;
15 }
16
上面代碼變量會以下圖入棧
棧在表達式中的應用
在第5步到第6步時,「-」的優先級小於「*」,便先進行出棧乘法運算,而「-」又小於從左到右的「+」的優先級,加法也出棧運算。
棧在括號匹配中的運用
例如「{[()]}」
從左到右,遇到左括號便入棧,當遇到「)」時,便從棧頂取出一個「(」,看是否匹配,若不匹配則爲非法,若匹配,則繼續向右掃描。
最後當掃描完全部的括號時,若棧爲空,則格式合法。非空,則非法。
用棧實現瀏覽器前進後退功能
使用連個棧,將首次瀏覽的頁面存入X
後退兩次回到a頁面
使用前進又回到了b頁面
這時候,你瀏覽了d頁面,則c頁面會在Y被清空,沒法回到c頁面了