文章目录
(一)栈(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/