棧:
英文名stack,特點是只允許訪問最后插入的那個元素,也就是LIFO(后進先出)
jdk中的stack源碼:
public class Stack<E> extends Vector<E> { //繼承Vector,Vector和ArrayList幾乎相同,都是通過數組保存數據,只不過方法有Synchronized修飾 public Stack() { } public E push(E item) { //push,也就是add,把數據保存得add到數組的末尾 addElement(item); return item; } public synchronized E pop() { //pop,也就是removeLast(),並且返回末尾值 E obj; int len = size(); //得到數組長度 obj = peek(); //得到數組末尾值 removeElementAt(len - 1); //刪除末尾元素 return obj; } public synchronized E peek() { //peek,返回末尾元素,只是查詢,不會刪除 int len = size(); if (len == 0) throw new EmptyStackException(); return elementAt(len - 1); } public boolean empty() { //empty,是否為空 return size() == 0; } public synchronized int search(Object o) { int i = lastIndexOf(o); if (i >= 0) { return size() - i; } return -1; } }
從上面源碼看到:
JDK源碼中的stack通過集成Vector實現,通過數組保存數據,一般使用push()、pop()、peek()來實現stack基本功能LIFO
stack有棧頂、棧底的概念,棧底是封閉的,數據只能從棧頂進出
自定義實現stack:
public class MyStack<E> { private int maxDepth; //棧深度 private Object[] elementData; //保存數據的數組 private int top; //棧頂的位置,空棧為-1 public MyStack() { this.maxDepth = 10; this.elementData = new Object[10]; this.top = -1; } public MyStack(int initialCapacity) { this.maxDepth = initialCapacity; this.elementData = new Object[initialCapacity]; this.top = -1; } public E push(E e) { ensureExplicitCapacity(top +1); elementData[++top] = e; return e; } public E pop(){ E e = peek(); elementData[top] = null; this.top--; return e; } public E peek() { return (E)elementData[top]; } private void ensureExplicitCapacity(int minCapacity) { if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { //擴容 int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - Integer.MAX_VALUE > 0) newCapacity = Integer.MAX_VALUE; this.maxDepth = newCapacity; elementData = Arrays.copyOf(elementData, maxDepth); } public int size() { return top + 1; }
public boolean isEmpty() {
return top == -1;
} }
public static void main(String[] args) { MyStack<Integer> stack = new MyStack<Integer>(); stack.push(1); stack.push(2); stack.push(3); int size = stack.size(); for (int i = 0; i < size; i++) { System.out.print(stack.peek() + " "); } System.out.println(""); for (int i = 0; i < size; i++) { System.out.print(stack.pop() + " "); } }
輸出結果: 3 3 3 3 2 1
這是使用ArrayList的思路實現Stack:
1、通過Object[]保存數據
2、擴展支持泛型
3、初始容量設置為10,擴容之后為原來容量的1.5倍
4、實現了push()、pop()、peek()和grow()
5、stack不適合使用for、foreach、Iterator,所以這里沒實現Iterable接口,因為peek()永遠只是棧頂的值,或者用pop()
PS:for循環的size,如果for循環使用pop(),不要寫成:
for (int i = 0; i < stack.size(); i++) { System.out.print(stack.pop() + " "); }
因為彈出元素,size會變化,無法彈出所有數據,可以使用測試代碼中那樣,或者while循環:
while (!stack.isEmpty()) { System.out.println(stack.pop());; }
到此為止一個stack的功能已經實現,從輸出結果看使用時沒問題的
實踐1:實現輸入字符串,逆序輸出
public static void main(String[] args) { MyStack<Character> stack = new MyStack<Character>(); char[] chars = getInput().toCharArray(); for (char c : chars) { stack.push(c); } while (!stack.isEmpty()) { System.out.print(stack.pop() + " ");; } } public static String getInput() { String s = null; try { InputStreamReader reader = new InputStreamReader(System.in); BufferedReader bufferedReader = new BufferedReader(reader); s = bufferedReader.readLine(); } catch (IOException e) { e.printStackTrace(); } return s; }
輸出結果:
實踐2:分隔符匹配
棧通常用於解析xml標簽或者HTML標簽,例如'{'要有個'}'進行匹配,通過把讀取字符,發現左分隔符push,發現右分隔符pop,然后對比是否
匹配,不匹配就報錯,如果棧中沒有左分隔符和右分隔符匹配,或者有左分隔符一直沒有被匹配,這些情況,也會報錯。
例如:a{12(u<d>1)f3}
代碼實現:
public static boolean isValid(String s) {
MyStack<Character> stack = new MyStack<>();
char[] chars = s.toCharArray();
for (char aChar : chars) {
switch (aChar) {
case '{':
stack.push('}');
continue;
case '<':
stack.push('>');
continue;
case '(':
stack.push(')');
continue;
default:
if (stack.isEmpty() || stack.pop() != aChar) {
return false;
}
}
}
return stack.isEmpty();
}
總結:
stack是概念上的工具,我們已經實現了兩個功能,能夠實現什么功能看個人實現,甚至Ctrl+Z撤銷的實現就是用stack實現的
stack入棧和出棧的時間復雜度都是O(1),棧的操作時間和棧中數據的個數沒有關系,不需要移動和比較,只不過我們這里實現了擴容,如果
發生擴容,會稍微影響一點
內容參考:
<Java數據結構和算法>