栈与队列:后进先出与先进先出的智慧

数据结构里的两种秩序,一个像叠盘子,一个像排队。

👋 你好,我是 Evan,一名计算机专业的学长,也是《大一突围》专栏的作者。学完数组和链表后,下一个该学什么?栈和队列------它们是"受限"的线性表,但正因为限制,它们解决了无数实际问题:函数调用、括号匹配、浏览器回退、任务调度......今天我用最直观的方式,带你彻底搞懂栈和队列的 push/pop 与 enqueue/dequeue。

欢迎来到 《大一突围》 专栏。

一、栈(Stack):后进先出(LIFO)

1.1 形象比喻

一叠盘子:你只能从顶部取(pop),也只在顶部放(push)。最后放上去的盘子,最先被拿走。

1.2 核心操作

  • push(item):将元素压入栈顶

  • pop():移除并返回栈顶元素

  • peek() / top():查看栈顶元素但不移除

  • isEmpty():判断栈是否为空

1.3 栈的图示

1.4 代码实现(Python + Java)

🐍 Python 示例(使用 list)
python 复制代码
class Stack:
    def __init__(self):
        self.items = []
    def push(self, item):
        self.items.append(item)
    def pop(self):
        if not self.is_empty():
            return self.items.pop()
    def peek(self):
        if not self.is_empty():
            return self.items[-1]
    def is_empty(self):
        return len(self.items) == 0

# 使用
s = Stack()
s.push(1); s.push(2); s.push(3)
print(s.pop())   # 3
print(s.peek())  # 2
☕ Java 示例(使用 Deque)
java 复制代码
import java.util.ArrayDeque;
import java.util.Deque;

public class StackDemo {
    public static void main(String[] args) {
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(1);   // 等价于 addFirst
        stack.push(2);
        stack.push(3);
        System.out.println(stack.pop());   // 3
        System.out.println(stack.peek());  // 2
    }
}

1.5 经典应用场景

  • 函数调用栈:调用方法时压栈,返回时出栈。

  • 括号匹配{ ( [ ) ] } 用栈检查是否配对。

  • 撤销操作(Undo):每次操作压栈,撤销时出栈。

  • 浏览器后退:访问新页面压栈,后退弹出。

二、队列(Queue):先进先出(FIFO)

2.1 形象比喻

排队买票:先来的先买到,后来的排在队尾。队头出队(dequeue),队尾入队(enqueue)。

2.2 核心操作

  • enqueue(item):将元素加入队尾

  • dequeue():移除并返回队头元素

  • front() / peek():查看队头元素

  • isEmpty():判断队列是否为空

2.3 队列的图示

2.4 代码实现(Python + Java)

🐍 Python 示例(使用 collections.deque)
python 复制代码
from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()
    def enqueue(self, item):
        self.items.append(item)   # 队尾入
    def dequeue(self):
        if not self.is_empty():
            return self.items.popleft()  # 队头出
    def front(self):
        if not self.is_empty():
            return self.items[0]
    def is_empty(self):
        return len(self.items) == 0

# 使用
q = Queue()
q.enqueue(1); q.enqueue(2); q.enqueue(3)
print(q.dequeue())   # 1
print(q.front())     # 2
☕ Java 示例(使用 Queue 接口)
java 复制代码
import java.util.LinkedList;
import java.util.Queue;

public class QueueDemo {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);   // enqueue
        queue.offer(2);
        queue.offer(3);
        System.out.println(queue.poll());   // dequeue → 1
        System.out.println(queue.peek());   // 查看队头 → 2
    }
}

2.5 经典应用场景

  • 任务调度(打印队列、进程调度)。

  • 广度优先搜索(BFS):借助队列逐层遍历图。

  • 消息队列(如 RabbitMQ、Kafka 的底层概念)。

  • 键盘缓冲区:你敲的字符先进入队列,系统依次处理。

三、栈 vs 队列:对比总结

代码操作对比表

四、进阶:用数组实现一个循环队列

常规队列用数组实现时,队头出队后前面的空间浪费。循环队列可以重用空间。

循环队列示意图

Python 实现(固定大小)

python 复制代码
class CircularQueue:
    def __init__(self, capacity):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0
        self.size = 0

    def enqueue(self, item):
        if self.size == self.capacity:
            raise Exception("Queue is full")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1

    def dequeue(self):
        if self.size == 0:
            raise Exception("Queue is empty")
        item = self.queue[self.front]
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return item

这个实现不仅更节省空间,而且广泛应用于操作系统和底层库。

五、实战练习:用栈实现队列

这是一道经典的面试题:仅使用两个栈实现队列的 enqueue 和 dequeue 操作

思路:一个栈作为输入栈(用于 enqueue),另一个栈作为输出栈(用于 dequeue)。当需要 dequeue 时,如果输出栈为空,就把输入栈的所有元素弹出并压入输出栈。

python 复制代码
class QueueUsingTwoStacks:
    def __init__(self):
        self.in_stack = []
        self.out_stack = []

    def enqueue(self, item):
        self.in_stack.append(item)

    def dequeue(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())
        if not self.out_stack:
            raise Exception("Queue is empty")
        return self.out_stack.pop()

# 测试
q = QueueUsingTwoStacks()
q.enqueue(1); q.enqueue(2); q.enqueue(3)
print(q.dequeue())  # 1
print(q.dequeue())  # 2
q.enqueue(4)
print(q.dequeue())  # 3
print(q.dequeue())  # 4

六、Evan 的一句话总结

  • :优先照顾"后来者",适合需要回溯、逆向的场景。

  • 队列:公平对待"先来者",适合需要顺序、缓存的场景。

掌握这两种数据结构,你就掌握了一半的算法基础。

❓ 问题:你有没有遇到过用栈或队列巧妙地解决某个实际问题的例子?或者你还分不清什么时候用栈、什么时候用队列?欢迎在评论区分享你的理解或困惑,我会选出 3 位同学,送出《栈和队列经典面试题集合》PDF。

📌 如果本文帮你理清了栈与队列的区别,请点 👍 赞 + 关注 ,本专栏 《大一突围》 持续输出数据结构与算法干货。

收藏本文,下次面试前回看,不再混淆 push/pop 和 enqueue/dequeue。

相关推荐
J2虾虾1 小时前
Spring AI Alibaba - Skills 技能
人工智能·python·spring
z落落1 小时前
C# 构造函数(无参/有参/重载/this)+析构函数(终结器)|GC 垃圾回收
java·开发语言·c#
带派擂总1 小时前
Python全栈开发 Day08_控制文件指针移动 异常捕获 推导式
python
XLYcmy2 小时前
面向Agent权限系统的快速审计工具
python·网络安全·ai·llm·飞书·agent·字节跳动
kkeeper~2 小时前
0基础C语言积跬步之自定义类型结构体
c语言·开发语言
z落落2 小时前
C# 字段与属性(get/set访问器、三种属性写法、只读属性)+属性拦截例子(get动态计算 + set数据校验)
开发语言·c#
范范@2 小时前
Python进阶 多线程、生成器与协程
python
影寂ldy2 小时前
C#栈和队列
开发语言·c#
SilentSamsara2 小时前
SQLAlchemy 2.x:异步 ORM 与数据库迁移 Alembic 完整指南
开发语言·数据库·python·sql·青少年编程·oracle·fastapi