今日收获:栈和队列的理论基础,用栈实现队列,用队列实现栈,栈的经典应用之消消乐
1. 栈-先进后出(Java)
(1)构造栈,以存储Integer类型为例,Stack类是泛型类
Stack<Integer> stack = new Stack<>();
Java中Stack类的源码: 底层可以用数组或链表实现
(2)常用方法
2. 队列-先进先出(Java)
(1)构造队列,以存储Integer为例,利用LinkedList实例化
Queue<Integer> queue=new LinkedList<>();
Java中的Queue是一个接口,LinkedList实现了这个接口
(2)常用方法:
3. 用栈实现队列
题目链接:232. - 力扣(LeetCode)
思路:需要用到两个栈,一个栈用来存储元素,另外一个栈用来弹出元素。弹出元素时,首先需要判断弹出栈是否为空,如果为空就将存储栈中的元素移入,不为空则优先弹出此栈中的元素。
方法:
class MyQueue {
Stack<Integer> stackIn=new Stack<>();
Stack<Integer> stackOut=new Stack<>();
int size=0;
public MyQueue() {
}
public void push(int x) {
stackIn.push(x);
size++;
}
public int pop() {
if (stackOut.isEmpty()){
while (!stackIn.isEmpty()){
int temp=stackIn.pop();
stackOut.push(temp);
}
}
size--;
return stackOut.pop();
}
public int peek() {
if (stackOut.isEmpty()){
while (!stackIn.isEmpty()){
int temp=stackIn.pop();
stackOut.push(temp);
}
}
return stackOut.peek();
}
public boolean empty() {
if (size==0){
return true;
}
return false;
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
总结:将栈中的元素弹出再进入另一个栈,此时另一个栈中的出栈顺序就和队列中相同了。
4. 用队列实现栈
题目链接:225. - 力扣(LeetCode)
思路:利用两个队列实现。添加元素时,首先将元素进入一个空队列1,原来存储元素的队列2全部清空,将元素转移到队列1中,然后队列1和队列2互换。
方法:
class MyStack {
Queue<Integer> queue1; // 和栈中的元素顺序相等
Queue<Integer> queue2;
public MyStack() {
queue1=new LinkedList<>();
queue2=new LinkedList<>();
}
public void push(int x) {
queue2.offer(x);
while (!queue1.isEmpty()){
queue2.offer(queue1.poll());
}
// 交换两个队列
Queue<Integer> temp=new LinkedList<>();
temp=queue1;
queue1=queue2;
queue2=temp;
}
public int pop() {
return queue1.poll();
}
public int top() {
return queue1.peek();
}
public boolean empty() {
return queue1.size()==0?true:false;
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
总结:队列1中的元素出队顺序和栈中相同。整体思路类似于汉诺塔:先用空的柱子存储新来的元素,然后将之前的元素转移到这个柱子上,最后交换两个柱子。
5. 栈的经典应用-消消乐
(1)有效括号
题目链接:20. - 力扣(LeetCode)
思路:创建一个栈,遍历字符串时遇到左括号则将相应的右括号压入栈中;遍历到右括号时首先判断栈是否为空,为空则不匹配(多余右括号),不为空则判断栈顶元素是否和当前遍历的右括号相等,不相等则说明括号不匹配;遍历结束后判断栈是否为空(不为空说明左括号多余)。注意字符串的长度必须是偶数,根据此条件剪枝。
方法:
class Solution {
public boolean isValid(String s) {
Stack<Character> stack=new Stack<>();
if (s.length()%2!=0){
return false; // 剪枝情况
}
// 遇到左括号就压入与之对应的右括号,遇到右括号判断栈顶元素是否与之相同
for (int i=0;i<s.length();i++){
char c=s.charAt(i);
if (c=='('){stack.push(')');}
else if (c=='{'){stack.push('}');}
else if (c=='['){stack.push(']');}
else if (c==')'||c=='}'||c==']'){
if (stack.isEmpty()||stack.pop()!=c){
return false;
}
}
}
if (!stack.isEmpty()){
return false;
}
return true;
}
}
总结:需要考虑三种不匹配的情况:右括号多余,括号不匹配,左括号多余。
(2)删除字符串中的重复相邻项
题目链接:1047. - 力扣(LeetCode)
思路:用栈存储遍历过的字符,除了第一个字符之外,其他字符都要和栈顶元素比较,如果相同就弹出栈顶元素,继续遍历下一个字符;否则将当前元素入栈。
方法:
class Solution {
public String removeDuplicates(String s) {
Stack<Character> stack=new Stack<>();
for (int i=0;i<s.length();i++){
char c=s.charAt(i);
if (!stack.isEmpty() && stack.peek()==c){
stack.pop();
continue;
}
stack.push(c);
}
char[] result=new char[stack.size()];
int k=stack.size()-1;
while (!stack.isEmpty()){
result[k--]=stack.pop();
}
return new String(result);
}
}
总结:栈非常适合判断相邻元素或者匹配的情况,像开心消消乐一样,遇到符合条件的就消除,不符合条件就入栈积压