標籤:資料結構 順序棧和鏈棧 java實現棧 線性表之棧
棧的定義:(特殊的線性表)
??僅在表的一端進行插入和刪除的線性表。允許插入、刪除的這一端稱為棧頂,另一端稱為棧底。表中沒有元素時稱為空白棧。
??被稱為後進先出的線性表(Last In First Out),簡稱 LIFO表,或被稱為先進後出的線性表(First In Last Out),簡稱 FILO表。
??棧更具儲存方式的不同分為兩種:順序棧和鏈棧。
順序棧:
- 和順序表一樣,順序棧也採用數組來存放資料元素;
- 為了保證棧底位置的不變,採用數組下標為0的位置作為順序棧的棧底。
- 而棧頂指標的最大值為capacity(棧的容量)-1;
- 當棧為空白棧時,採用棧頂指標指向-1表示。
下面借圖(來自http://www.nowamagic.net/librarys/veda/detail/2271)示範一下:
對於順序棧,資料元素的進棧操作解釋如下:
- 棧頂指標top先自增1,給需要進棧的元素騰出記憶體空間;
- 然後給top對應的數組元素賦值,data[top] = e。size加1
出棧的操作則相反,如下:
- 先獲得棧頂指標對應的數組元素的值;
- 然後棧頂指標top減1。size減1
下面是我的Java代碼實現:
package com.phn.stack;/** * @author 潘海南 * @Email [email protected] * @TODO 順序棧 * @date 2015年7月20日 */public class FOArrayStack<E> { //初始化預設棧的儲存容量 private static final int DEFUALT_CAPACITY = 100; //棧中儲存資料元素的數組 private Object[] data = null; //棧的實際大小 private int size; //棧的棧頂指標 private int top; //棧的實際容量 private int capacity; /** * @TODO 無參建構函式,初始化棧 */ public FOArrayStack(){ this(DEFUALT_CAPACITY); } /** * @TODO 帶參建構函式,初始化棧 * @param initialCapacity 初始化棧的容量 */ public FOArrayStack(int initialCapacity) { this.capacity = initialCapacity; this.data = new Object[initialCapacity]; this.size = 0; this.top = this.size-1; } /** * @TODO 壓入資料元素到棧中 * @param e 資料元素 * @return true */ public boolean push(E e){ this.validateCapacity(); this.top++; this.data[top]=e; this.size++; return true; } /** * @TODO 驗證棧的實際大小是否已經到達棧實際容量的極限 */ private void validateCapacity() { if(this.top==this.capacity-1){ throw new RuntimeException("此棧已滿!最大容量="+this.capacity); } } /** * @TODO 擷取棧頂元素,並沒有將其彈出棧 * @return e 資料元素 */ public E peek(){ if(this.isEmpty()){ throw new RuntimeException("此棧為空白棧!"); }else{ Object e = new Object(); e = this.data[this.top]; return (E)e; } } /** * @TODO 擷取棧頂元素並彈出棧 * @return e 資料元素 */ public E pop(){ E e = this.peek(); this.top--; this.size--; return e; } /** * @TODO 清空棧 * @return true */ public boolean clear(){ while(this.top>=0){ this.data[this.top]=null; this.top--; this.size--; } return true; } @Override public String toString() { StringBuffer sb = new StringBuffer("["); if(this.top!=-1){ sb.append(this.data[this.top]); int temp = this.top-1; while(temp>=0){ sb.append(", "+this.data[temp]); temp--; } } sb.append("]"); return sb.toString(); } /** * @TODO 判斷棧是否為空白 * @return true空 or false不為空白 */ public boolean isEmpty(){//或者用長度表示 if(this.top==-1){ return true; } return false; } /** * @TODO 棧的實際大小 * @return */ public int size(){ return this.size; }}
我的測試代碼:
package com.phn.stack;/** * @author 潘海南 * @Email [email protected] * @TODO 順序棧測試 * @date 2015年7月20日 */public class FOArrayStackTest { public static void main(String[] args) { FOArrayStack<String> foas = new FOArrayStack<String>(6); foas.push("元素1"); foas.push("元素2"); foas.push("元素3"); foas.push("元素4"); foas.push("元素5"); System.out.println(foas); foas.pop(); System.out.println(foas); String s = foas.peek(); System.out.println(s); System.out.println(foas); foas.clear(); System.out.println(foas); System.out.println(foas.isEmpty()); }}
測試結果:
下面擴充一下順序棧,兩棧共用空間:
??順序棧具有單向延伸的特性,在一個程式中如果同時使用了具有相同資料類型的兩個棧時可考慮使用一個數組來儲存這兩個棧,其中棧1的棧底設在該數組的始端,棧2的棧底設在該數組的尾端,兩個棧都從各自的端點向數組中部延伸,只有在兩個棧的棧頂在數組空間的某一位置相遇時才會產生“上溢”。棧1在入棧操作時棧頂指標top1++,出棧操作時top1–;棧2在入棧操作時棧頂指標top2–,出棧操作時top2++。
鏈棧:
- 同單鏈表類似,只不過鏈棧只能從棧頂插入資料;
- 因此可以將單鏈表的頭指標作為鏈棧的棧頂指標,而去掉單鏈表的頭結點,這樣得到的就是鏈棧了。
- 鏈棧不同順序棧,鏈棧沒有容量限制,不存在棧滿的情況。空鏈棧的定義是棧頂指標指向空null。
對於鏈棧,插入(壓棧)操作push解釋如下:
- 將需要插入的資料元素放入一個建立立的節點temp中,將temp的next指向topNode;
- 將topNode賦值為temp。size加1
刪除(彈出)操作pop解釋如下:
- 取出topNode的資料元素e;
- 然後將topNode指向topNode的next節點;size減1。
下面是我的Java實現代碼:
package com.phn.stack;/** * @author 潘海南 * @Email [email protected] * @TODO 鏈棧 * @date 2015年7月20日 */public class FOLinkedStack<E> { //棧頂指標 private FOLinkedNode<E> topNode = null; //棧的長度 private int size; /** * @TODO 無參建構函式,初始化鏈棧 */ public FOLinkedStack() { this.size = 0; } /** * @TODO 擷取棧的長度 * @return */ public int size() { return this.size; } /** * @TODO 壓入資料元素到棧中 * @param e 要壓入的資料元素 * @return true */ public boolean push(E e) { FOLinkedNode<E> temp = new FOLinkedNode<E>(); temp.setE(e); temp.addNext(this.topNode); this.topNode = temp; this.size++; return true; } /** * @TODO 擷取棧頂元素,並沒有彈出,還存在棧中 * @return e 擷取到的資料元素 */ public E peek(){ if(this.isEmpty()){ throw new RuntimeException("鏈棧為空白!"); }else{ E e = this.topNode.getE(); return e; } } /** * @TODO 彈出棧頂資料元素,不在棧中了 * @return e 彈出的資料元素 */ public E pop(){ E e = this.peek(); this.topNode = this.topNode.next; this.size--; return e; } /** * @TODO 棧是否為空白 * @return true空 or false不為空白 */ public boolean isEmpty(){ if(this.topNode==null){ return true; } return false; } @Override public String toString() { StringBuffer sb = new StringBuffer("["); if(this.topNode!=null){ sb.append(this.topNode.getE()); FOLinkedNode<E> temp =new FOLinkedNode<E>(); temp = this.topNode.next; while(temp!=null){ sb.append(","+temp.getE()); temp = temp.next; } } sb.append("]"); return sb.toString(); }}
鏈棧節點類:
package com.phn.stack;public class FOLinkedNode<E> { private E e;// 結點中存放的資料 FOLinkedNode() { } FOLinkedNode(E e) { this.e = e; } FOLinkedNode<E> next;// 用來指向該結點的下一個結點 // 設定下一節點的值 void addNext(FOLinkedNode<E> node) { next = node; } public E getE() { return e; } public void setE(E e) { this.e = e; } @Override public String toString() { return e.toString(); } }
下面是我的測試代碼:
public static void main(String[] args) { FOLinkedStack<String> fols = new FOLinkedStack<String>(); System.out.println(fols.isEmpty()); fols.push("元素1"); System.out.println(fols); System.out.println(fols.size()); System.out.println(fols.peek()); System.out.println(fols); System.out.println(fols.pop()); System.out.println(fols.isEmpty()); System.out.println(fols); System.out.println(fols.size()); fols.push("元素4"); fols.push("元素2"); fols.push("元素5"); fols.push("元素3"); fols.push("元素6"); System.out.println(fols); System.out.println(fols.size()); System.out.println(fols.isEmpty()); }
測試:
順序棧和鏈棧的比較:
- 兩種棧都是“先進後出”(或者稱之為“後進先出”)的特點,只能在棧頂進行操作,所以時間複雜度為常量O(1);
- 順序棧初始化需要分配儲存空間,分配過大則浪費,過小則溢出;
- 鏈棧初始化不需要分配空間,不過需要在分配一個指標域,存在結構性開銷,但是沒有長度限制。
應用建議:
??如果棧的使用過程中元素變化不可預料,有時很小,有時非常大,那麼最好是用鏈棧,反之,如果它的變化在可控範圍內,建議使用順序棧會更好一些。
使用棧的原因:棧的引入簡化了程式設計的問題,劃分了不同關注層次,使得思考範圍縮小,更加聚焦於我們要解決的問題核心。
著作權聲明:本文為博主原創文章,如需轉載請註明出處並附上連結,謝謝。
Java資料結構-線性表之棧(順序棧和鏈棧)