“是否还在为数据先进先出、后进先出的顺序而烦恼?”
栈(Stack)和队列(Queue)是计算机科学中最基础且最重要的数据结构之一。无论是浏览器的前进后退、打印机的任务处理,还是括号匹配、迷宫求解,栈和队列的身影无处不在。
这篇文章将从栈和队列的基础概念出发,结合代码案例、经典问题和实际应用场景,手把手教你掌握这两个数据结构的核心知识!文末还有常见问题解答和实战技巧分享哦~✨
栈是一种只能在一端进行插入或删除操作的线性数据结构,遵循“先进后出”(First In Last Out, FILO)的原则。想象一下洗碗池的水槽,每次只能从最上面拿走盘子。
将元素压入栈顶。
public void push(int value) {
if (top == size - 1) {
System.out.println(" 栈已满,无法入栈!");
return;
}
top++;
arr[top] = value;
}
从栈顶取出元素。
public int pop() {
if (top == -1) {
System.out.println(" 栈为空,无法出栈!");
return -1;
}
int poppedValue = arr[top];
top--;
return poppedValue;
}
查看栈顶元素而不移除它。
public int peek() {
if (top == -1) {
System.out.println(" 栈为空!");
return -1;
}
return arr[top];
}
队列是一种只能在一端插入而在另一端删除的线性数据结构,遵循“先进先出”(First In First Out, FIFO)的原则。想象一下银行排队系统,先来的人先办理业务。
将元素添加到队尾。
public void enqueue(int value) {
if (rear == size - 1) {
System.out.println(" 队列已满,无法入队!");
return;
}
rear++;
arr[rear] = value;
}
从队头取出元素。
public int dequeue() {
if (front > rear) {
System.out.println(" 队列为空,无法出队!");
return -1;
}
int dequeuedValue = arr[front];
front++;
return dequeuedValue;
}
查看队头元素而不移除它。
public int peekFront() {
if (front > rear) {
System.out.println(" 队列为空!");
return -1;
}
return arr[front];
}
验证字符串中的括号是否正确匹配是一个经典的栈应用问题。
(
或 {
或 [
)时入栈。public boolean isValid(String s) {
Stack 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();
}
迷宫求解可以通过栈实现深度优先搜索(DFS)。
class Point {
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public List solveMaze(int[][] maze, Point start, Point end) {
Stack stack = new Stack<>();
stack.push(start);
maze[start.x][start.y] = 1; // 标记已访问
while (!stack.isEmpty()) {
Point current = stack.peek();
if (current.x == end.x && current.y == end.y) {
return stack;
}
// 尝试四个方向
boolean canMove = false;
// 上
if (current.x > 0 && maze[current.x - 1][current.y] == 0) {
stack.push(new Point(current.x - 1, current.y));
maze[current.x - 1][current.y] = 1;
canMove = true;
continue;
}
// 下
if (current.x < maze.length - 1 && maze[current.x + 1][current.y] == 0) {
stack.push(new Point(current.x + 1, current.y));
maze[current.x + 1][current.y] = 1;
canMove = true;
continue;
}
// 左
if (current.y > 0 && maze[current.x][current.y - 1] == 0) {
stack.push(new Point(current.x, current.y - 1));
maze[current.x][current.y - 1] = 1;
canMove = true;
continue;
}
// 右
if (current.y < maze[0].length - 1 && maze[current.x][current.y + 1] == 0) {
stack.push(new Point(current.x, current.y + 1));
maze[current.x][current.y + 1] = 1;
canMove = true;
continue;
}
if (!canMove) {
stack.pop();
}
}
return null; // 无解
}
约瑟夫环问题是一个经典的队列应用问题。
public int josephusProblem(int n, int k) {
Queue queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
queue.add(i);
}
while (queue.size() > 1) {
for (int i = 0; i < k - 1; i++) {
int front = queue.poll();
queue.add(front);
}
queue.poll(); // 移除第k个人
}
return queue.poll();
}
public class ArrayStack {
private int[] arr;
private int top;
private int size;
public ArrayStack(int size) {
this.size = size;
arr = new int[size];
top = -1;
}
public void push(int value) { /* 实现同上 */ }
public int pop() { /* 实现同上 */ }
public int peek() { /* 实现同上 */ }
}
public class ArrayQueue {
private int[] arr;
private int front;
private int rear;
private int size;
public ArrayQueue(int size) {
this.size = size;
arr = new int[size];
front = -1;
rear = -1;
}
public void enqueue(int value) { /* 实现同上 */ }
public int dequeue() { /* 实现同上 */ }
}
class Node {
int value;
Node next;
Node(int value) {
this.value = value;
this.next = null;
}
}
public class LinkedListStack {
private Node top;
private int size;
public LinkedListStack() {
top = null;
size = 0;
}
public void push(int value) {
Node newNode = new Node(value);
if (top == null) {
top = newNode;
} else {
newNode.next = top;
top = newNode;
}
size++;
}
public int pop() {
if (top == null) {
System.out.println(" 栈为空!");
return -1;
}
int poppedValue = top.value;
top = top.next;
size--;
return poppedValue;
}
}
class Node {
int value;
Node next;
Node(int value) {
this.value = value;
this.next = null;
}
}
public class LinkedListQueue {
private Node front;
private Node rear;
private int size;
public LinkedListQueue() {
front = null;
rear = null;
size = 0;
}
public void enqueue(int value) {
Node newNode = new Node(value);
if (front == null) {
front = newNode;
rear = newNode;
} else {
rear.next = newNode;
rear = newNode;
}
size++;
}
public int dequeue() {
if (front == null) {
System.out.println(" 队列为空!");
return -1;
}
int dequeuedValue = front.value;
front = front.next;
size--;
return dequeuedValue;
}
}
特性 | 栈(Stack) | 队列(Queue) |
---|---|---|
数据访问顺序 | 后进先出(FILO) | 先进先出(FIFO) |
应用场景 | 递归、回溯、括号匹配等 | 排队系统、任务调度等 |
插入/删除效率 | O(1) | O(1) |
空间复杂度 | O(n) | O(n) |
使用两个栈实现浏览器的前进后退功能。
public class BrowserHistory {
Stack historyStack = new Stack<>();
Stack forwardStack = new Stack<>();
public void visit(String url) {
historyStack.push(url);
forwardStack.clear();
}
public String back() {
if (!historyStack.isEmpty()) {
String backUrl = historyStack.pop();
forwardStack.push(backUrl);
return backUrl;
}
return null;
}
public String forward() {
if (!forwardStack.isEmpty()) {
String forwardUrl = forwardStack.pop();
historyStack.push(forwardUrl);
return forwardUrl;
}
return null;
}
}
使用队列实现打印机的任务处理。
public class PrinterQueue {
Queue taskQueue = new LinkedList<>();
public void addTask(String task) {
taskQueue.add(task);
System.out.println(" 任务已添加:" + task);
}
public void processTasks() {
while (!taskQueue.isEmpty()) {
String task = taskQueue.poll();
System.out.println(" 正在打印:" + task);
try {
Thread.sleep(1000); // 模拟打印时间
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 打印完成:" + task);
}
}
}
通过这篇文章,你已经掌握了栈和队列的核心知识及其在实际中的应用。如果你有任何疑问或想看到更多实战案例,请在评论区留言!我会逐一回复并分享更多技巧。
P.S. 如果你有任何技术问题或学习困惑,欢迎随时在评论区留言!我会尽力帮助你!