数据结构——栈

在计算机科学中,栈(Stack)是一种基础且极为重要的线性数据结构。它的操作规则可以用四个字概括:后进先出(LIFO, Last In First Out)。就像一叠盘子------你总是先取最上面的那个,而最下面的盘子只有等到上面的全部拿走之后才能被触及。

本文将从栈的基本概念、核心操作、两种常见实现方式(顺序栈和链栈)、典型应用场景以及复杂度分析等方面,带你全面了解栈。


一、什么是栈?

栈是一种操作受限 的线性表,它只允许在表的一端进行插入和删除操作,这一端被称为栈顶 (Top),另一端则称为栈底 (Bottom)。栈中没有元素时称为空栈

可以想象一个羽毛球筒:放入羽毛球(入栈)只能从顶部放入,取出羽毛球(出栈)也只能从顶部取出,最先放入的羽毛球反而最后才能被取出。

二、栈的基本操作

栈通常提供以下核心操作:

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

  • pop():弹出栈顶元素,并返回该元素。若栈为空,则操作非法。

  • peek() / top():获取栈顶元素,但不弹出。

  • is_empty():判断栈是否为空。

  • size():返回栈中元素的个数。

这些操作的时间复杂度均为 O(1),因为无论栈中有多少元素,我们总是只处理栈顶元素。

三、栈的实现方式

栈可以通过两种基本方式实现:顺序栈(基于数组)链式栈(基于链表)

1. 顺序栈(Array-based Stack)

使用一段连续的内存空间(如 Python 列表)存储栈元素,并维护一个变量 top 来指示栈顶位置。

python 复制代码
class ArrayStack:
    def __init__(self, capacity=10):
        self._data = [None] * capacity
        self._top = -1          # 栈顶索引,-1 表示空栈
        self._capacity = capacity

    def push(self, item):
        if self._top == self._capacity - 1:
            self._resize()      # 动态扩容
        self._top += 1
        self._data[self._top] = item

    def pop(self):
        if self.is_empty():
            raise IndexError("pop from empty stack")
        item = self._data[self._top]
        self._data[self._top] = None   # 便于垃圾回收
        self._top -= 1
        return item

    def peek(self):
        if self.is_empty():
            raise IndexError("peek from empty stack")
        return self._data[self._top]

    def is_empty(self):
        return self._top == -1

    def size(self):
        return self._top + 1

    def _resize(self):
        self._capacity *= 2
        new_data = [None] * self._capacity
        for i in range(self.size()):
            new_data[i] = self._data[i]
        self._data = new_data

优点 :实现简单,访问速度快(连续内存)。
缺点:需要预先分配或动态扩容,扩容时有一定开销。

2. 链式栈(Linked Stack)

使用链表节点来存储元素,每个节点包含数据域和指向下一个节点的指针,栈顶位于链表头部。

python 复制代码
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

class LinkedStack:
    def __init__(self):
        self._top = None
        self._size = 0

    def push(self, item):
        new_node = Node(item)
        new_node.next = self._top
        self._top = new_node
        self._size += 1

    def pop(self):
        if self.is_empty():
            raise IndexError("pop from empty stack")
        item = self._top.value
        self._top = self._top.next
        self._size -= 1
        return item

    def peek(self):
        if self.is_empty():
            raise IndexError("peek from empty stack")
        return self._top.value

    def is_empty(self):
        return self._top is None

    def size(self):
        return self._size

优点 :不需要连续内存,动态扩展无扩容成本。
缺点:每个元素需额外存储指针,内存占用稍高,且访问效率略低于数组(缓存不友好)。

四、栈的经典应用

栈在计算机科学中应用极广,以下列举几个典型场景:

1. 函数调用栈

几乎所有编程语言在实现函数调用时都使用了栈。每次函数调用,系统会在调用栈上压入一个"栈帧"(包含局部变量、返回地址等);函数返回时,弹出该栈帧。递归函数的深度受限于栈空间大小,递归过深会导致"栈溢出"。

2. 表达式求值与括号匹配

  • 括号匹配 :编译器检查代码中的括号 ()[]{} 是否成对出现且嵌套正确。遇到左括号压栈,遇到右括号时弹出栈顶,若匹配则继续,否则报错。

  • 表达式转换:中缀表达式转后缀表达式(逆波兰表达式)使用栈来调整运算符优先级。

3. 浏览器的前进后退

浏览器使用两个栈实现:一个栈记录已访问页面,另一个栈记录可前进的页面。后退时,将当前页面从第一个栈弹出并压入第二个栈;前进时反向操作。

4. 撤销操作(Undo)

许多编辑器中的撤销功能通过栈来实现。每次用户操作(输入、删除等)都将状态压入栈,撤销时弹出最近的状态。

五、复杂度分析

操作 顺序栈(平均) 链式栈
push O(1) * O(1)
pop O(1) O(1)
peek O(1) O(1)
is_empty O(1) O(1)

*顺序栈的 push 在数组未满时为 O(1),扩容时需复制元素(摊还分析下仍为 O(1))

空间复杂度:顺序栈需要 O(n) 的连续空间,链式栈需要 O(n) 的空间加上 n 个指针的额外开销。

六、栈的局限与思考

栈的 LIFO 特性既是它的优势,也是它的局限。它只适合解决"后进先出"相关问题,对于需要随机访问或先进先出(FIFO)的场景,则应该选择其他数据结构(如队列)。

尽管如此,栈作为最基础的数据结构之一,是学习其他高级数据结构(如递归树、深度优先搜索等)的基石。掌握栈的实现和应用,有助于我们更好地理解程序的运行机制。

七、总结

本文介绍了栈的核心概念:

  • 栈是一种后进先出的线性数据结构,操作受限。

  • 基本操作:pushpoppeekis_empty 等,时间复杂度均为 O(1)。

  • 可以通过数组(顺序栈)或链表(链式栈)实现,各有优缺点。

  • 栈在函数调用、表达式求值、撤销操作等场景中扮演关键角色。

栈虽然简单,却无处不在。理解栈,就掌握了程序世界中"后进先出"的规则,也为自己后续学习更复杂的数据结构和算法打下了坚实的基础。

相关推荐
左左右右左右摇晃2 小时前
数据结构——树
数据结构·笔记
Book思议-2 小时前
【数据结构实战】川剧 “扯脸” 与栈的 LIFO 特性 :用 C 语言实现 3 种栈结构
c语言·数据结构·算法·
chudonghao2 小时前
[UE学习笔记][基于源码] 理解 Gameplay
c++·笔记·学习·ue5
云淡风轻~窗明几净3 小时前
关于TSP的海岸线猜想:SeaLine算法的逐层法(不同于逐点法)
数据结构·算法·动态规划·模拟退火算法
菜鸟小九3 小时前
hot100(91-100)
数据结构·算法·排序算法
平常心cyk4 小时前
Python基础快速复习——集合和字典
开发语言·数据结构·python
左左右右左右摇晃4 小时前
数据结构——数组
数据结构·笔记·算法
左左右右左右摇晃4 小时前
数据结构——队列
数据结构
nainaire4 小时前
速通LeetCode hot100——(1~9 哈希,双指针,滑动窗口)
c++·笔记·算法·leetcode