文章目錄
(一)棧(Stack)的簡介
(二)棧(Stack)的設計
(三)棧(Stack)的實現
(四)棧(Stack)的源碼
(五)應用舉例:瀏覽器的前進和後退
(六)練習:有效的括號01
(七)練習:有效的括號02
(八)練習:有效的括號03
(九)作業題
(一)棧(Stack)的簡介
(二)棧(Stack)的設計
注意:對於棧,我們需要頻繁的操作尾部元素,所以使用動態數組和鏈表(雙向鏈表)都可以,對尾部元素進行增刪操作的複雜度都是O(1)
(三)棧(Stack)的實現
-
第一種實現方法:繼承,如下:
import com.zzq.list.ArrayList; public class Stack<E> extends ArrayList<E> { public void push(E element) { add(element); } public E pop() { return remove(size - 1); } public E top() { return get(size - 1); } }
效果如下:
繼承會導致Stack多了很多不需要的方法,我們一般不會直接繼承,會使用下面第二種方法 -
第二種方法:組合(讓ArrayList成爲Stack的一部分,或者說用Stack給ArrayList做一層封裝),如下:
import com.zzq.list.ArrayList; import com.zzq.list.List; public class Stack<E> { private List<E> list = new ArrayList<>(); public void clear() { list.clear(); } public int size() { return list.size(); } public boolean isEmpty() { return list.isEmpty(); } public void push(E element) { list.add(element); } public E pop() { return list.remove(list.size() - 1); } public E top() { return list.get(list.size() - 1); } }
效果也是一樣的,這裏不再展示了
(四)棧(Stack)的源碼
其實JDK官方也提供了一個Stack類,如下:
它繼承了Vector,是線程安全的
(五)應用舉例:瀏覽器的前進和後退
注意:對用戶可見的是棧頂,所以瀏覽器正在顯示的就是棧頂頁面
瀏覽器實現前進和後退功能需要用到兩個棧,流程如下:
注意:此時輸入新的網址,右邊的棧就要被清空(防止前進)
其實軟件的撤銷(Undo)和恢復(Redo)功能也是同樣的原理,如下:
(六)練習:有效的括號01
https://leetcode-cn.com/problems/valid-parentheses/
思路:我們最先拿到的{
要與最後拿到的}
做配對,很符合棧的先進後出
流程:
- 比如字符串爲
{([])}
,遇到左字符就入棧 - 假設左字符全部入棧後,棧爲
{([
(棧頂爲[
) - 此時遇到右字符
]
就用棧頂跟它比較,比較完就出棧
具體說明如下:
(七)練習:有效的括號02
代碼實現如下:
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (c == '(' || c == '{' || c == '[') { //左括號
stack.push(c);
} else { //右括號
if (stack.isEmpty()) return false;//首先判斷棧是否爲空
char left = stack.pop();//已經出棧了,只能判斷是否合法,不合法就返回false
if (left == '(' && c != ')') return false;
if (left == '{' && c != '}') return false;
if (left == '[' && c != ']') return false;
}
}
return stack.isEmpty();
}
(八)練習:有效的括號03
上述的代碼還可以優化,如下:
private static HashMap<Character, Character> map = new HashMap<>();
static {
// key(左括號) -> value(右括號)
map.put('(', ')');
map.put('{', '}');
map.put('[', ']');
}
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (map.containsKey(c)) { //左括號
stack.push(c);
} else { //右括號
if (stack.isEmpty()) return false;//首先判斷棧是否爲空
if (c != map.get(stack.pop())) return false;
}
}
return stack.isEmpty();
}
(九)作業題
856. 括號的分數:https://leetcode-cn.com/problems/score-of-parentheses/
150. 逆波蘭表達式求值:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
瞭解:前綴表達式、中綴表達式和後綴表達式
224. 基本計算器:https://leetcode-cn.com/problems/basic-calculator/