棧
棧的基本特點
- 先進後出, 後進先出(LIFO, Last in First Out)
- 除頭尾節點外, 每個節點都有前驅和後繼節點
棧的基本操作
- 壓棧(push), 將資料放入棧頂。
- 彈棧(pop), 將資料從棧頂移除。
通過下圖來認識一下棧:
棧的一些應用
舉兩個例子
- 編輯器都有一個undo(即撤銷)操作, 比如我們在IDEA中寫程式碼寫錯了, 想要撤銷到修改之前該怎麼辦呢? 就需要執行undo操作了。
對於編輯器來說undo操作的原理是什麼呢?
其實就是依靠棧(Stack)來維護的, 如下過程:
* 輸入**沉迷**, 編輯器就會記錄這個動作。這個記錄方式其實就是把這個動作放入棧中, 記錄了**沉迷**
* 然後輸入**學習**, 編輯器的棧會在記錄一次, 以同樣的方式壓入棧中。
* 下面如果我要輸入**無法**但卻輸入了**無天**, 棧中也記錄了這個動作。發現輸入出錯後, 需要撤銷這個操作, 這裡需要怎麼做?
* 其實是從編輯器的這個棧中取出棧頂元素, 而棧頂記錄的操作是輸入出錯的"無天", 執行撤銷就是刪除這兩個字。也就是把這兩個字從棧頂中移除。
複製程式碼
- 在比如說我們系統程式的呼叫, 比如有A,B,C三個方法, A呼叫了B, B呼叫了C, 那麼當A方法中執行到呼叫B方法的時候會將A方法壓入系統棧, 當B呼叫C的時候會把B方法壓入棧, 當C方法執行完畢後, 會從棧中找出上一次執行中斷的方法再次執行, 直到棧中為空。如下圖:
棧的實現
- 入棧
- 出棧
- 棧頂元素
- 棧的長度
- 棧是否為空
我們從上面幾個點來看看怎麼設計一個Stack。
這裡需要注意下, 我們可能會用佇列, 陣列或者是連結串列等結構來建立一個棧, 但是棧的功能都是一樣的, 只不過是底層資料結構不同, 所以這裡抽出一個介面來實現不同的型別的棧。
public interface Stack<E> {
// 獲取棧的長度
int getSize();
// 判斷棧是否為空
boolean isEmpty();
// 資料壓棧(add)
void push(E e);
// 資料彈棧(remove)
E pop();
// 獲取棧頂元素
E peek();
}
複製程式碼
/**
* Created by Joker on 19/9/14.
* 利用陣列來實現棧的功能
*/
public class ArrayStack<E> implements Stack<E> {
Array<E> array ;
public ArrayStack(int capacity) {
this.array = new Array<>(capacity);
}
public ArrayStack() {
this.array = new Array<>();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public void push(E e) {
array.addLast(e);
}
@Override
public E pop() {
return array.removeLast();
}
@Override
public E peek() {
return array.getLast();
}
@Override
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("Stack: [");
for (int i = 0; i < array.getSize(); i ++) {
buff.append(array.get(i));
if (i < array.getSize() - 1)
buff.append(",");
}
buff.append("] top");
return buff.toString();
}
}
複製程式碼
現在已經對棧有了基本的瞭解, 我們來看一道題目。
給定一個只包括 '(',')','{','}','[',']' 的字串,判斷字串是否有效。
有效字串需滿足:
- 左括號必須用相同型別的右括號閉合。
- 左括號必須以正確的順序閉合。
例子:
輸入: "()"
輸出: true
輸入: "()[]{}" 輸出: true
輸入: "([)]" 輸出: false
輸入: "{[]}" 輸出: true
輸入: "(]" 輸出: false
下面的示例程式碼比較笨重, 使用棧來解決
public boolean isValid(String s) {
java.util.Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '[' || c == '{' || c == '(') {
stack.push(c);
} else {
if (stack.isEmpty())
return false;
Character character = stack.pop();
if (c == ')' && character != '(')
return false;
if (c == '}' && character != '{')
return false;
if (c == ']' && character != '[')
return false;
}
}
return stack.isEmpty()
}
複製程式碼