栈经典题目
括号匹配问题
20 有效的括号
Deque有三种用途:
- 普通队列(一端进另一端出):
Queue queue = new LinkedList()
或Deque deque = new LinkedList()
- 双端队列(两端都可进出)
Deque deque = new LinkedList()
- 堆栈
Deque deque = new LinkedList()
注意:Java堆栈Stack类已经过时,Java官方推荐使用Deque替代Stack使用。Deque堆栈操作方法:push()、pop()、peek()。
根据题目可以分析出三种不匹配的情况为:
- 情况一:左括号多余
- 情况二:右括号多余
- 情况三:括号不多余,但是括号类型不匹配
对于情况一、情况二,可以直接根据字符串长度是否是偶数来进行判断
对于第三种情况可以使用栈来进行判断。
代码实现:
class Solution {
public boolean isValid(String s) {
if(s.length() % 2 != 0) return false;
Deque<Character> st = new LinkedList<>();
for(int i = 0;i < s.length();i++){
if(s.charAt(i)=='('){//这里直接使用相对应的右括号入栈,后续弹栈时操作比较方便
st.push(')');
}else if(s.charAt(i) == '['){
st.push(']');
}else if(s.charAt(i)=='{'){
st.push('}');
}else if(st.isEmpty() || s.charAt(i) != st.peek()){
return false;
}else{
st.pop();
}
// else{
// if(!st.isEmpty() && s.charAt(i) == st.peek()){
// st.pop();
// }else{
// return false;
// }
// }
}
return st.isEmpty();
}
}
字符串去重问题
1047 删除字符串中的所有相邻重复项
本题使用栈来保持已经遍历过的元素,然后拿当前遍历到的元素和栈顶元素进行比较,如果相等,直接删除栈顶元素,如果不相等,就将当前遍历的元素入栈。
class Solution {
public String removeDuplicates(String s) {
Deque<Character> st = new LinkedList<>();
for(int i = 0;i < s.length();i++){
if(st.isEmpty() || s.charAt(i) != st.peek()){
st.push(s.charAt(i));
}else{
st.pop();
}
}
String str = "";
while(!st.isEmpty()){
str = st.pop()+str;
}
return str;
}
}
class Solution {//使用StringBuilder
public String removeDuplicates(String s) {
StringBuilder sb = new StringBuilder();
int top = -1;
for(int i = 0;i < s.length();i++){
char ch = s.charAt(i);
if(top >= 0 && sb.charAt(top) == ch ){
sb.deleteCharAt(top);
top--;
}else{
sb.append(ch);
top++;
}
}
return sb.toString();
}
}
逆波兰表达式问题
- 遇到数字则入栈
- 遇到字符串则弹出前两个栈顶元素进行运算
class Solution {
public int evalRPN(String[] tokens) {
Deque<Integer> st = new LinkedList<>();
for(String item : tokens){
if("+".equals(item)){
st.push(st.pop()+st.pop());
}else if("-".equals(item)){//num2-num1
st.push(-st.pop()+st.pop());
}else if("*".equals(item)){
st.push(st.pop()*st.pop());
}else if("/".equals(item)){
int tmp = st.pop();
int tmp2 = st.pop();
st.push(tmp2/tmp);
}else{
st.push(Integer.valueOf(item));
}
}
return st.pop();
}
}
队列经典题目
滑动窗口最大值问题
239 滑动窗口的最大值
本题主要用到了单调队列
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
//结果数组
int[] result = new int[nums.length-k+1];
MyQueue mq = new MyQueue();
int count = 0;
//先放前三个元素到单调队列中
for(int i = 0;i < k;i++){
mq.push(nums[i]);
}
result[count++] = mq.peek();
//从单调队列中移除最左端元素,放入最右端元素
for(int i = k;i < nums.length;i++){
mq.pop(nums[i-k]);
mq.push(nums[i]);
result[count++] = mq.peek();
}
return result;
}
}
//维护一个从大到小排序的单调队列
class MyQueue{
Deque<Integer> que = new LinkedList<>();
//弹出元素
public void pop(int val){
if(!que.isEmpty() && val == que.peek()){
que.poll();
}
}
//插入元素
public void push(int val){
while(!que.isEmpty() && val > que.getLast()){
que.removeLast();
}
que.add(val);
}
//弹出窗口内的最大值
public int peek(){
return que.peek();
}
}
347 前K个高频元素
本题主要用到了优先级队列,本质上就是堆,本题使用小顶堆来维护前K个高频元素
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//创建一个map来统计元素的频率
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i < nums.length;i++){
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
}
//构建小顶堆
PriorityQueue<int[]> pq = new PriorityQueue<>((pair1,pair2)->pair1[1]-pair2[1]);
for(Map.Entry<Integer,Integer> entry : map.entrySet()){
//如果小于k,直接添加
if(pq.size() < k){
pq.add(new int[]{entry.getKey(),entry.getValue()});
}else{
if(entry.getValue() > pq.peek()[1]){
pq.poll();
pq.add(new int[]{entry.getKey(),entry.getValue()});
}
}
}
//倒序输出数组
int[] result = new int[k];
for(int i = k-1;i >= 0;i--){
result[i] = pq.peek()[0];
pq.poll();
}
return result;
}
}