在學習的過程中有時會遇到一種資料結構也就是棧,這裡對棧做個詳細的瞭解。在完善這篇部落格的過程中主要參考了這兩篇博文:http://www.cnblogs.com/QG-whz/p/5170418.html#_label1_1和http://blog.csdn.net/javazejian/article/details/53362993在這裡註明出處,所涉及到的代碼皆由我用java實現。第一次修改日期是2017.8.02下午 棧簡介 棧的特點
棧(Stack)是一種線性儲存結構,也可以稱做一種操作受限的特殊的線性表,其插入和刪除操作只允許線上性表的一端進行,一般而言,把允許操作的一端稱為棧頂(Top),不可操作的一端稱為棧底(Bottom),棧頂也就是線性序列的末端。
總結有如下兩個特點:
1, 棧中的資料元素遵守”後進先出”( Last In Out First)的原則,簡稱LIFO結構。
2, 限定只能在棧頂進行插入和刪除操作。 棧的相關概念
1,棧頂與棧底:允許元素插入與刪除的一端稱為棧頂(TOP),另一端稱為棧底(BOTTOM)。
2,壓棧:棧的插入操作,叫做進棧,也稱壓棧、入棧(PUSH)。
3,彈棧:棧的刪除操作,也叫做出棧(POP)。 棧的基本運算
1,入棧(push)
在壓棧的過程中,棧頂的位置一直在”向上“移動,而棧底是固定不變的。
2,出棧(pop)
出棧的順序為3、2、1 ,順序與入棧時相反,這就是所謂的”後入先出“。
在彈棧的過程中,棧頂位置一直在”向下“移動,而棧底一直保持不變。
3,求棧的大小(size)
4,判斷棧是否為空白(isEmpty)
5,擷取棧頂元素的值(top) 棧的實現方式 java中Stack類的使用
/** * Stack類 * 棧:桶型或箱型資料類型,後進先出,相對堆Heap為二叉樹類型,可以快速定位並操作 * Stack<E>,支援泛型 * public class Stack<E> extends Vector<E> * Stack的方法調用的Vector的方法,被synchronized修飾,為安全執行緒(Vector也是) * Stack methods: * push : 把項壓入堆棧頂部 ,並作為此函數的值返回該對象 * pop : 移除堆棧頂部的對象,並作為此函數的值返回該對象 * peek : 查看堆棧頂部的對象,,並作為此函數的值返回該對象,但不從堆棧中移除它 * empty : 測試堆棧是否為空白 * search : 返回對象在堆棧中的位置,以 1 為基數 * size:返回棧的大小 * */
package ca.map; import java.util.Stack; public class StackX { public static void main(String[] args) { stackMethod(); } //stack operate public static void stackMethod(){ //定義一個Integer泛型的Stack Stack<Integer> stack = new Stack<Integer>(); System.out.println("建立棧stack是否為空白 : "+(stack.empty() ? "空" : stack.size())); //push : 把項壓入堆棧頂部,傳回值泛型指定的類型 //此處將1到5壓入棧中 stack.push(1); stack.push(2); stack.push(3); stack.push(4); stack.push(5); System.out.println("將1到5按順序壓入棧中後為:"+stack); //empty : 測試堆棧是否為空白,size() == 0,傳回值boolean System.out.println("值為1~5的棧中stack是否為空白 : "+(stack.empty() ? "空" : stack.size())); //search : 返回對象在堆棧中的位置,以 1 為基數,參數:search(Object o) ,傳回值int int oStack = stack.search(3); System.out.println("尋找棧stack中對象3的位置elementId為 : "+oStack); //peek : 查看堆棧頂部的對象,但不從堆棧中移除它,傳回值泛型指定的類型 int topElement =stack.peek(); System.out.println("查看stack的棧頂元素為 : "+topElement); System.out.println("peek操作stack後為 : "+stack); //pop : 移除堆棧頂部的對象,並作為此函數的值返回該對象,傳回值泛型指定的類型 int oRemove = stack.pop(); System.out.println("移除stack棧頂的元素為 : "+oRemove); System.out.println("pop操作移除stack棧頂元素後為 : "+stack); } }
測試結果
建立棧stack是否為空白 : 空將1到5按順序壓入棧中後為:[1, 2, 3, 4, 5]值為1~5的棧中stack是否為空白 : 5尋找棧stack中對象3的位置elementId為 : 3查看stack的棧頂元素為 : 5peek操作stack後為 : [1, 2, 3, 4, 5]移除stack棧頂的元素為 : 5pop操作移除stack棧頂元素後為 : [1, 2, 3, 4]
若集合為空白,返回 []
若集合不為空白則 [ 加上迭代元素 加上 , 最後集合無元素加上 ] eg:[1, 2, 3, 4]
棧按照儲存結構有兩種實現方式。 數組實現
棧的建立和常用方法代碼:
package stack;import java.util.EmptyStackException;public class MyStackArray<T> { public T[] array = null; // 存放元素的數組 public int initalSize = 5; // 棧的容量大小,預設為10 public int top = -1; // 棧頂指標 ,-1表示空棧,因為數組下標從0開始。 public int curSize; //表示棧的當前實際容量是多大 public MyStackArray() { this(5); } @SuppressWarnings("unchecked") public MyStackArray(int Size) { // 建構函式,用於初始化棧大小 if (Size >= 0) { this.initalSize= Size; // 棧的容量為該值 array = (T[]) new Object[Size]; // 存放棧元素數組大小的值也為該值 top = -1; // 如果棧頂指標指向-1,表示空棧, curSize = 0; } else { throw new RuntimeException("初始化大小不能小於0:" + Size); } } // 判斷該棧是否為空白 public boolean isEmpty() { return top == -1 ? true : false; // 如果棧頂指標指向-1,表示空棧 } // 擴充棧的的容量 @SuppressWarnings("unchecked") public void expandCapacity() { // 如果需要拓展的容量比現在數組的容量還小,則無需擴容 T[] old = array; array = (T[]) new Object[curSize * 2 + 1]; initalSize = curSize * 2 + 1; // 複製元素 for (int i = 0; i < top; i++) array[i] = old[i]; } // 進棧,先判斷棧是否已經滿了 public void push(T data) { if (top == initalSize-1) { expandCapacity(); // 如果棧已經滿了,則擴充容量 } // 從棧頂添加元素 array[++top] = data; // 沒滿的話top指向的下一個元素為該元素,數組擴充 curSize++; } // 出棧,首先判斷是否為空白棧,為空白則報異常 public T pop() { if (isEmpty()) new EmptyStackException(); curSize--; return array[top--]; // 返回要刪除的元素 } // 查看棧頂元素但不移除 public T peek() { if (isEmpty()) new EmptyStackException(); return array[top]; } //返回對象在堆棧中的位置,以 1 為基數 public int search(T data){ int i = top; while(top != -1){ if(peek() != data){ top--; }else{ break; } } int result = top+1; top = i; //top返回棧頂位置 return result; }}
測試代碼:
package stack;public class Stack { public static void main(String[] args) { MyStackArray<Integer> s = new MyStackArray<Integer>(); s.push(1); s.push(5); s.push(3); s.push(4); System.out.println("棧的容量"+ s.initalSize); System.out.println("棧頂元素"+s.peek()); System.out.println("搜尋元素的位置" +s.search(5)); System.out.println("出棧元素是"+s.pop()); System.out.println("出棧一次後棧頂" +s.peek()); s.push(8); s.push(9); System.out.println("棧頂元素"+s.peek()); System.out.println("棧的容量"+ s.initalSize); s.push(10); System.out.println("棧頂元素"+s.peek()); System.out.println("棧的容量"+ s.initalSize); }}
測試結果
棧的容量5棧頂元素4搜尋元素的位置2出棧元素是4出棧一次後棧頂3棧頂元素9棧的容量5 //注意,在這個地方由於增加元素超過總容量,所以自動擴容棧頂元素10棧的容量11
單鏈表實現
鏈表類代碼
package stack;public class Node<T> { public T val; public Node<T> next = null; public Node(T val, Node<T> next) { //構造方法,建立一個新節點 this.val = val; this.next = next; }}
單鏈表組成的棧代碼
package stack;public class MyStackList<T> { public Node<T> top; // 棧頂元素 public int curSize; // 判斷該棧是否為空白 public boolean isEmpty() { return curSize == 0 ? true : false; } // 進棧,先判斷棧是否已經滿了 public void push(T val) { if (val == null) { System.out.println("新加節點的資料不能為null"); } if (top == null) { top = new Node<T>(val, null); // 如果當前為空白棧,無節點,則進來的節點為第一個節點 } else if (top.val == null) { top.val = val; // 如果當前top不為null,但是其中的資料為null,則只需把當前的資料賦給棧頂就好 } else { Node<T> newNode = new Node<T>(val, top); top = newNode; // 更新棧頂 } curSize++; } // 出棧,首先判斷是否為空白棧,為空白則報異常 public T pop() { if (isEmpty()) { System.out.println("棧為空白"); } T oldData = top.val; top = top.next; curSize--; return oldData; } // 查看棧頂元素但不移除 public T peek() { if (isEmpty()) { System.out.println("棧為空白"); } return top.val; } // 返回對象在堆棧中的位置,以 1 為基數 public int search(T data) { int count = curSize; Node<T> temp = top; while (temp != null) { if (temp.val == data) return count; count--; temp = temp.next; } return -100000; }}
測試代碼
package stack;public class ListStack { public static void main(String[] args) { MyStackList<Integer> s = new MyStackList<Integer>(); s.push(1); s.push(5); s.push(3); s.push(3); System.out.println("棧的容量"+ s.curSize); s.pop(); System.out.println("棧的容量"+ s.curSize); System.out.println("棧頂元素"+s.peek()); System.out.println("搜尋元素的位置" +s.search(1)); System.out.println("出棧元素是"+s.pop()); System.out.println("出棧兩次後棧頂" +s.peek()); s.push(8); s.push(9); System.out.println("棧頂元素"+s.peek()); System.out.println("棧的容量"+ s.curSize); s.push(10); System.out.println("棧頂元素"+s.peek()); System.out.println("棧的容量"+ s.curSize); }}
測試結果
棧的容量4棧的容量3棧頂元素3搜尋元素的位置1出棧元素是3出棧兩次後棧頂5棧頂元素9棧的容量4棧頂元素10棧的容量5
兩種實現的時間複雜度比較
順序棧複雜度如下:
鏈式棧複雜度如下:
由此可知棧的主要操作都可以在常數時間內完成,這主要是因為棧只對一端進行操作,而且操作的只是棧頂元素。 LinkedList實現棧
import java.util.LinkedList;/** * 基於LinkedList實現棧 * 在LinkedList實力中只選擇部分基於棧實現的介面 */public class StackList<E> { private LinkedList<E> ll = new LinkedList<E>(); //入棧 public void push(E e){ ll.addFirst(e); } //查看棧頂元素但不移除 public E peek(){ return ll.getFirst(); } //出棧 public E pop(){ return ll.removeFirst(); } //判空 public boolean empty(){ return ll.isEmpty(); } //列印棧元素 public String toString(){ return ll.toString(); }}