深入浅出Java算法:栈与队列处理类问题
栈和队列是两种重要的线性数据结构,在算法问题中应用广泛。下面我将用通俗易懂的方式介绍常见的栈和队列处理问题及其解决方案。
一、栈篇
1. 有效的括号
问题:判断字符串中的括号是否有效匹配
java
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(' || c == '[' || c == '{') {
stack.push(c);
} else {
if (stack.isEmpty()) return false;
char top = stack.pop();
if ((c == ')' && top != '(') ||
(c == ']' && top != '[') ||
(c == '}' && top != '{')) {
return false;
}
}
}
return stack.isEmpty();
}
2. 最小栈
问题:设计一个支持push、pop、top操作,并能检索最小元素的栈
java
class MinStack {
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
stack = new Stack<>();
minStack = new Stack<>();
}
public void push(int val) {
stack.push(val);
if (minStack.isEmpty() || val <= minStack.peek()) {
minStack.push(val);
}
}
public void pop() {
if (stack.pop().equals(minStack.peek())) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
}
3. 逆波兰表达式求值
问题:根据逆波兰表示法(后缀表达式)求表达式的值
java
public int evalRPN(String[] tokens) {
Stack<Integer> stack = new Stack<>();
for (String token : tokens) {
if ("+-*/".contains(token)) {
int b = stack.pop();
int a = stack.pop();
switch (token) {
case "+": stack.push(a + b); break;
case "-": stack.push(a - b); break;
case "*": stack.push(a * b); break;
case "/": stack.push(a / b); break;
}
} else {
stack.push(Integer.parseInt(token));
}
}
return stack.pop();
}
4. 每日温度
问题:给定每日温度列表,返回需要等待多少天才能有更高温度的天数
java
public int[] dailyTemperatures(int[] temperatures) {
Stack<Integer> stack = new Stack<>();
int[] result = new int[temperatures.length];
for (int i = 0; i < temperatures.length; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int prevIndex = stack.pop();
result[prevIndex] = i - prevIndex;
}
stack.push(i);
}
return result;
}
二、队列篇
1. 用栈实现队列
问题:使用栈实现队列的push、pop、peek和empty操作
java
class MyQueue {
private Stack<Integer> inStack;
private Stack<Integer> outStack;
public MyQueue() {
inStack = new Stack<>();
outStack = new Stack<>();
}
public void push(int x) {
inStack.push(x);
}
public int pop() {
if (outStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
return outStack.pop();
}
public int peek() {
if (outStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
return outStack.peek();
}
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
}
2. 滑动窗口最大值
问题:给定数组和滑动窗口大小,返回每次窗口滑动时的最大值
java
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) return new int[0];
int[] result = new int[nums.length - k + 1];
Deque<Integer> deque = new ArrayDeque<>();
for (int i = 0; i < nums.length; i++) {
// 移除不在窗口范围内的元素
while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
deque.pollFirst();
}
// 移除比当前元素小的元素,保持队列递减
while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
deque.pollLast();
}
deque.offerLast(i);
// 当窗口形成时记录最大值
if (i >= k - 1) {
result[i - k + 1] = nums[deque.peekFirst()];
}
}
return result;
}
3. 设计循环队列
问题:设计一个循环队列实现
java
class MyCircularQueue {
private int[] queue;
private int head;
private int tail;
private int size;
private int capacity;
public MyCircularQueue(int k) {
queue = new int[k];
head = 0;
tail = -1;
size = 0;
capacity = k;
}
public boolean enQueue(int value) {
if (isFull()) return false;
tail = (tail + 1) % capacity;
queue[tail] = value;
size++;
return true;
}
public boolean deQueue() {
if (isEmpty()) return false;
head = (head + 1) % capacity;
size--;
return true;
}
public int Front() {
return isEmpty() ? -1 : queue[head];
}
public int Rear() {
return isEmpty() ? -1 : queue[tail];
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == capacity;
}
}
三、栈与队列综合问题
1. 柱状图中最大的矩形
问题:在柱状图中找出能勾勒出的最大矩形的面积
java
public int largestRectangleArea(int[] heights) {
Stack<Integer> stack = new Stack<>();
int maxArea = 0;
int i = 0;
while (i <= heights.length) {
int h = (i == heights.length) ? 0 : heights[i];
if (stack.isEmpty() || h >= heights[stack.peek()]) {
stack.push(i++);
} else {
int top = stack.pop();
int width = stack.isEmpty() ? i : i - stack.peek() - 1;
maxArea = Math.max(maxArea, heights[top] * width);
}
}
return maxArea;
}
2. 用队列实现栈
问题:使用队列实现栈的push、pop、top和empty操作
java
class MyStack {
private Queue<Integer> queue;
public MyStack() {
queue = new LinkedList<>();
}
public void push(int x) {
queue.offer(x);
int size = queue.size();
// 将新元素前面的所有元素重新入队
for (int i = 0; i < size - 1; i++) {
queue.offer(queue.poll());
}
}
public int pop() {
return queue.poll();
}
public int top() {
return queue.peek();
}
public boolean empty() {
return queue.isEmpty();
}
}
总结
栈和队列问题的解决技巧:
-
栈的典型应用:
- 括号匹配问题
- 表达式求值
- 单调栈解决边界问题
- 深度优先搜索(DFS)
-
队列的典型应用:
- 广度优先搜索(BFS)
- 滑动窗口问题
- 任务调度
-
常用技巧:
- 使用辅助栈/队列(如最小栈问题)
- 双栈实现队列/双队列实现栈
- 单调栈/单调队列解决极值问题
- 循环队列的实现技巧
记住解决这类问题的关键:
- 理解栈(LIFO)和队列(FIFO)的基本特性
- 画图辅助理解操作过程
- 考虑边界条件(空栈/队列、单个元素等)
- 合理选择数据结构和算法
掌握这些基础问题的解法,能够帮助你更好地解决更复杂的算法问题!