数据结构九——栈
文章出處:極客時間《數(shù)據(jù)結構和算法之美》-作者:王爭。該系列文章是本人的學習筆記。
1棧的定義
1.1 棧的定義
棧:后進者先出,先進著后出。就像一碟盤子,如果拿走一個盤子,拿走的一定是最后放上去的那個。棧是一種操作受限的線性表,只允許在一端插入和刪除。本質上來說就是一個操作不方便的數(shù)組或者鏈表。
棧存在的意義是什么?數(shù)據(jù)結構本身就是對特定場景的抽象,要求操作不多不少,正好滿足場景需求。過多的接口會帶來操作不可控,進而操作結果不可預計。
1.2 實現(xiàn)棧
我們可以使用數(shù)組實現(xiàn)一個棧,稱為順序棧;也可以用鏈表實現(xiàn)一個棧,稱為鏈式棧。
/*** 數(shù)組實現(xiàn)的棧,在一端插入和刪除,那就對數(shù)組的最后一個元素操作吧*/ public class ArrayStack {//存儲數(shù)據(jù)的值private int[] items;//容量private int n;//存儲元素個數(shù)private int size;/**** @param capacity* 容量*/public ArrayStack(int capacity){this.n = capacity;items = new int[n];}/*** 添加元素,如果還有空間則添加成功,返回true。否則返回false。* @param val* @return*/public boolean push(int val){if(this.size >=n) return false;items[size++] = val;return true;}/*** 棧內元素個數(shù)* @return*/public int size(){return this.size;}/*** 刪除棧頂元素。如果當前棧內沒有元素,則拋出異常。在調用pop之前先調用size()吧。* @return*/public int pop(){if(size==0) throw new IllegalArgumentException("棧目前沒有任何元素,不能彈出元素");return items[--size];} }復雜度分析。push,pop時間復雜度為O(1),即使鏈表實現(xiàn)的棧也一樣。空間復雜度式O(n)。需要大小為n的數(shù)組存儲棧內元素。
1.3 支持動態(tài)擴容的棧
上面的實現(xiàn)中,當棧內沒有空間的時候,就不能再插入元素。如果實現(xiàn)一個空間不受限的棧呢?這是一個數(shù)組自動擴容、自動減少空間的過程。我們只需要判斷在數(shù)組滿了的時候申請一個2倍容量的數(shù)組,將原有元素拷貝過去。
當棧內元素只有數(shù)組容量1/4的時候,將數(shù)組容量縮小到一半。當然對內存空間不敏感的應用可以不用做這一步。
/*** 添加元素* @param val*/public void push(int val){if(this.size >=n) {grow();}items[size++] = val;}private void grow() {int newSize = 2*size;int[] newItmes = new int[newSize];System.arraycopy(this.items,0,newItmes,0,size);this.n = newItmes.length;this.items = newItmes;}接下來我們分析一下支持動態(tài)擴容的棧入棧時間復雜度是多少。入棧操作的平均時間復雜度用攤還分析法。為了分析方便,我們先做以下假設:
1 只有擴容操作,擴容為原來數(shù)組2倍大小;
2 只有push,沒有pop操作;
3 定義不涉及內存搬移的入棧操作為simple-push。
如果當前數(shù)組大小為K,當再有新的數(shù)據(jù)入棧的時候,需要申請一個大小為2K的數(shù)組,并且有K次數(shù)據(jù)遷移的操作。但是接下來的K-1次入棧操作,就無需申請內存和遷移數(shù)據(jù)。
如圖所示,第K次入棧,需要的K次遷移操作,可以均攤到到未來K-1次入棧操作。所以這K次操作,平均下來每次是一次數(shù)據(jù)遷移和一個simple-push。時間復雜度O(1)。
2棧的應用
2.1 棧在函數(shù)調用中的應用
2.2 棧在表達式求值中的應用
2.3 棧在括號匹配中的應用
3 瀏覽器支持前進后退操作
瀏覽器前進、后退操作是這樣的。當你訪問頁面 a->b->c之后,你可以按后退按鈕回到頁面b,再繼續(xù)按前進按鈕到頁面c。
我們使用兩個棧X、Y來完成瀏覽器的前進后退操作。當訪問頁面的時候,我們按照順序將頁面入棧X。當按后退按鈕的時候,從X棧取出元素,入棧Y。當按前進按鈕的時候,從Y棧取出元素,入棧X。當X棧為空的時候,后退按鈕不能用。當Y棧為空的時候,前進按鈕不能用。
例如初始:
X
Y
訪問頁面a:
X:a
Y
訪問頁面b:
X:a,b
Y
訪問頁面c:
X:a,b,c
Y
按后退按鈕,展示頁面b:
X:a,b
Y:c
按后退按鈕,展示頁面a:
X:a
Y:c,b
按前進按鈕,從頁面a到頁面b:
X:a,b
Y:c
這個時候,你訪問了新頁面d,這時候前進按鈕應該不能用,因為d的下一個頁面還沒有產(chǎn)生;按后退按鈕的話應該回到b;所以c頁面不能通過前進后退轉到,需要清空Y棧:
X:a,b,d
Y:
4 思考
Java虛擬機中有堆棧的概念。棧內用來存儲臨時變量和方法調用,堆內存儲Java對象。那么Java虛擬機中的棧和這里的棧,概念一樣嗎?
答:不一樣。數(shù)據(jù)結構中的棧是對場景的抽象,是抽象的數(shù)據(jù)機構。
JVM使用的內存分為代碼區(qū)、靜態(tài)數(shù)據(jù)區(qū)和動態(tài)數(shù)據(jù)區(qū)。
代碼區(qū):存放方法的二進制代碼,控制代碼區(qū)代碼執(zhí)行切換。
靜態(tài)數(shù)據(jù)區(qū):全局變量、靜態(tài)變量,常量(包含final修飾的和String)。
動態(tài)數(shù)據(jù)區(qū):分為堆區(qū)和棧區(qū)。堆區(qū)存放對象,該對象的引用存放在棧區(qū)。棧區(qū)存放運行方法的形參、局部變量和返回值。
總結
- 上一篇: DBCP|C3P0参数详解
- 下一篇: java 代码走查_代码走查检查表(JA