棧:
英文名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數據結構和算法>
