引言

栈和队列是两种基本的线性数据结构,在算法和系统设计中应用广泛。本文系统讲解栈和队列的原理和经典问题。

一、栈(Stack)

1.1 栈的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Stack<T> {
private Object[] data;
private int top;
private static final int DEFAULT_CAPACITY = 10;

public Stack() {
data = new Object[DEFAULT_CAPACITY];
top = -1;
}

public void push(T item) {
if (top == data.length - 1) {
resize();
}
data[++top] = item;
}

@SuppressWarnings("unchecked")
public T pop() {
if (isEmpty()) {
throw new EmptyStackException();
}
return (T) data[top--];
}

@SuppressWarnings("unchecked")
public T peek() {
if (isEmpty()) {
throw new EmptyStackException();
}
return (T) data[top];
}

public boolean isEmpty() {
return top == -1;
}

private void resize() {
data = Arrays.copyOf(data, data.length * 2);
}
}

1.2 有效的括号(LeetCode 20)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
Map<Character, Character> map = new HashMap<>();
map.put(')', '(');
map.put('}', '{');
map.put(']', '[');

for (char c : s.toCharArray()) {
if (map.containsKey(c)) {
if (stack.isEmpty() || stack.pop() != map.get(c)) {
return false;
}
} else {
stack.push(c);
}
}

return stack.isEmpty();
}

1.3 最小栈(LeetCode 155)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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() {
int val = stack.pop();
if (val == minStack.peek()) {
minStack.pop();
}
}

public int top() {
return stack.peek();
}

public int getMin() {
return minStack.peek();
}
}

二、队列(Queue)

2.1 队列实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Queue<T> {
private Object[] data;
private int front;
private int rear;
private int size;
private static final int DEFAULT_CAPACITY = 10;

public Queue() {
data = new Object[DEFAULT_CAPACITY];
front = 0;
rear = -1;
size = 0;
}

public void offer(T item) {
if (size == data.length) {
resize();
}
rear = (rear + 1) % data.length;
data[rear] = item;
size++;
}

@SuppressWarnings("unchecked")
public T poll() {
if (isEmpty()) {
throw new NoSuchElementException();
}
T item = (T) data[front];
front = (front + 1) % data.length;
size--;
return item;
}

public boolean isEmpty() {
return size == 0;
}

private void resize() {
Object[] newData = new Object[data.length * 2];
for (int i = 0; i < size; i++) {
newData[i] = data[(front + i) % data.length];
}
data = newData;
front = 0;
rear = size - 1;
}
}

2.2 用栈实现队列(LeetCode 232)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MyQueue {
private Stack<Integer> input;
private Stack<Integer> output;

public MyQueue() {
input = new Stack<>();
output = new Stack<>();
}

public void push(int x) {
input.push(x);
}

public int pop() {
peek();
return output.pop();
}

public int peek() {
if (output.isEmpty()) {
while (!input.isEmpty()) {
output.push(input.pop());
}
}
return output.peek();
}

public boolean empty() {
return input.isEmpty() && output.isEmpty();
}
}

三、单调栈

3.1 每日温度(LeetCode 739)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] result = new int[n];
Stack<Integer> stack = new Stack<>();

for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int prevIndex = stack.pop();
result[prevIndex] = i - prevIndex;
}
stack.push(i);
}

return result;
}

四、优先队列

4.1 前K个高频元素(LeetCode 347)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> count = new HashMap<>();
for (int num : nums) {
count.put(num, count.getOrDefault(num, 0) + 1);
}

PriorityQueue<Integer> heap = new PriorityQueue<>(
(a, b) -> count.get(a) - count.get(b)
);

for (int num : count.keySet()) {
heap.offer(num);
if (heap.size() > k) {
heap.poll();
}
}

int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = heap.poll();
}

return result;
}

总结

栈和队列核心:

  1. 栈:后进先出(LIFO)
  2. 队列:先进先出(FIFO)
  3. 单调栈:维护单调性
  4. 优先队列:堆实现

参考资料