Python中怎么用列表实现一个栈和队列?

来源:语言推理作者:孙悟空头衔:草根站长
导读:本期聚焦于小伙伴创作的《Python中怎么用列表实现一个栈和队列?》,敬请观看详情。把列表当容器做栈和队列时,最容易被忽略的是头部插入删除带来的性能拐点。列表尾部追加弹出是O(1),但用insert(0)或pop(0)模拟队列会让后续元素整体平移,数据量上万时延迟陡增。栈可直接用append和pop,逻辑清晰且不需要额外依赖。队列若坚持用列表,应搭配collections.deque避免复制开销,或在应用层用双指针环形缓冲降低搬移。下文给出可运行示例,并对比两种结构在增删、遍历、内存上的差异,帮助你按场景选型而不是盲目封装。

在Python里,列表本身就是可变序列,借助它提供的方法可以很轻量地模拟栈和队列这两种基础线性结构。栈遵循后进先出,队列遵循先进先出,二者核心差异只在于元素的取出位置。理解列表底层连续存储的特性,才能写出既正确又高效的代码。

Python中怎么用列表实现一个栈和队列?

用列表实现栈

栈的操作非常单纯:只能在某一端压入和弹出。Python列表的尾部追加和删除都是 amortized O(1),因此把列表末尾当作栈顶是最合理的做法。我们只需使用 append 方法压栈,用 pop 方法弹栈,不需要指定索引。

下面给出一个简单的栈封装示例,包含判空、大小和查看栈顶的辅助方法,避免在业务代码里直接散落列表操作。

class ListStack:
    def __init__(self):
        self._data = []

    def push(self, item):
        # 尾部追加,时间复杂度O(1)
        self._data.append(item)

    def pop(self):
        if self.is_empty():
            raise IndexError('pop from empty stack')
        # 弹出尾部元素,时间复杂度O(1)
        return self._data.pop()

    def peek(self):
        if self.is_empty():
            return None
        return self._data[-1]

    def is_empty(self):
        return len(self._data) == 0

    def size(self):
        return len(self._data)

if __name__ == '__main__':
    s = ListStack()
    s.push(10)
    s.push(20)
    print(s.pop())   # 输出20
    print(s.peek())  # 输出10

这种实现的优点是直观、无需引入标准库之外的东西,而且由于列表缓存局部性好,小规模数据下速度极快。缺点是一旦列表前期容量不足,append 可能触发底层数组扩容拷贝,不过均摊之后影响可以忽略。

如果你的栈需要频繁跨线程使用,列表本身不是线程安全的,要在外层加锁。但在单线程算法题或脚本工具里,列表栈已经足够。

用列表实现队列

队列要求从一端进、另一端出。若直接在列表头部用 pop(0) 或 insert(0, item) 来模拟,每次操作都会让后面所有元素在内存中向前或向后平移一位,时间复杂度退化为 O(n)。当数据量到十万级,这种写法会明显卡顿。

一种折中方案是仍然用列表,但把尾部当队尾、头部当队头,并接受 O(n) 的弹出成本;另一种更好的做法是改用 collections.deque,它在两端操作都是 O(1)。下面先用纯列表写出版本,再给出 deque 版本做对比。

class ListQueue:
    def __init__(self):
        self._data = []

    def enqueue(self, item):
        # 尾部入队
        self._data.append(item)

    def dequeue(self):
        if not self._data:
            raise IndexError('dequeue from empty queue')
        # 头部出队,会触发元素平移,O(n)
        return self._data.pop(0)

    def is_empty(self):
        return len(self._data) == 0

# 推荐:使用deque实现高效队列
from collections import deque

class DequeQueue:
    def __init__(self):
        self._data = deque()

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

    def dequeue(self):
        if not self._data:
            raise IndexError('dequeue from empty queue')
        return self._data.popleft()

    def is_empty(self):
        return len(self._data) == 0

从代码可以看出,DequeQueue 只是把底层存储换成了 deque,接口完全不变,但 dequeue 从 O(n) 降到 O(1)。在任务调度、广度优先搜索等需要频繁进出队的场景,这种替换能大幅降低延迟。

如果出于某些限制必须用列表且数据量较大,也可以用环形缓冲思路:维护 head 和 tail 指针,配合固定长度列表复用槽位,避免整体搬移。不过那已经接近手写 deque,日常开发中直接引用标准库更稳妥。

性能与选型对比

为了更清楚地看到差异,我们把两种结构在常见操作上的表现整理成表。这里的复杂度指单次操作随数据规模 n 的变化趋势。

结构入栈/入队出栈出队(列表头)随机访问
列表栈O(1)O(1)不适用O(1)
列表队列(pop(0))O(1)不适用O(n)O(1)
deque队列O(1)不适用O(1)O(1)近似

可以看到,列表栈在功能与效率上都没有短板;列表硬模拟队列则在出队上存在性能陷阱。如果你的代码只是几十个元素的小缓冲,用 pop(0) 也无伤大雅,但写成公共组件时最好用 deque 或注明复杂度。

另外要注意,列表和 deque 都不是线程安全的,多线程生产消费请配合 queue.Queue 或加锁,不要因为底层用了 deque 就误以为原子。

常见误区与小结

一个常见误区是认为列表的 insert(0, x) 和 pop(0) 跟尾部一样快,实际上连续存储决定了头部变动必然搬移。另一个误区是把栈和队列的方法混用,比如用列表栈却从索引0取元素,破坏了封装语义。

实践中,建议把数据结构用类包一层,对外只暴露 push、pop 或 enqueue、dequeue,这样未来从列表切换到 deque 甚至自定义环形队列时,调用方无需改动。Python标准库已经提供了完备原语,理解列表实现有助于看懂这些原语的成本边界,而不是重复造轮子。

Python列表栈实现队列实现修改时间:2026-08-05 18:03:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。