栈和队列 | 豆包MarsCode AI刷题

95 阅读4分钟

在使用豆包MarsCode AI刷题过程中,我对栈和队列这两个数据结构进行了深入的总结与分析。

栈(Stack)

栈是一种后进先出的线性数据结构。栈只允许在一端进行操作,即栈顶。常见的栈操作包括:

  1. push() :将元素添加到栈顶。时间复杂度:O(1)。
  2. pop() :移除栈顶元素,并返回该元素。时间复杂度:O(1)。
  3. empty() :检查栈是否为空。时间复杂度:O(1)。
  4. top():返回栈顶元素。时间复杂度:O(1)。

栈的应用场景非常广泛,特别是在表达式求值,如52题、168题。

逆波兰式表达式求值如下:

  1. 中缀表达式转后缀表达式(逆波兰表达式) :首先将中缀表达式转换为后缀表达式,利用栈处理运算符的优先级和括号嵌套。这样,操作数和运算符的顺序能够满足后缀表达式计算的需求。

    • 运算符优先级:加法和减法的优先级较低,乘法和除法的优先级较高。括号内的运算需要优先计算。

    • 转换规则

      • 如果遇到一个数字,直接输出。

      • 如果遇到一个运算符,根据运算符的优先级与栈顶运算符的优先级进行比较:

        • 如果当前运算符优先级高于栈顶运算符,压栈。
        • 如果当前运算符优先级低于或等于栈顶运算符,弹出栈顶运算符并输出,直到栈顶运算符的优先级低于当前运算符。
      • 如果遇到左括号,直接压栈。

      • 如果遇到右括号,弹出栈内运算符直到遇到左括号。

  2. 计算后缀表达式:一旦中缀表达式被转化为后缀表达式,就可以用栈进行计算了。

    • 逐个扫描后缀表达式的元素:

      • 如果是操作数,直接入栈。
      • 如果是运算符,从栈中弹出两个操作数,进行运算,将结果压栈。
    • 最后栈中会剩下一个元素,即运算结果。

括号匹配思路如下:

  1. 扫描表达式:逐个扫描表达式的字符。

    • 如果遇到左括号([{,则将其压栈。

    • 如果遇到右括号)]},则需要检查栈顶元素是否为对应的左括号:

      • 如果栈为空或栈顶元素不是对应的左括号,则表示括号不匹配,返回false
      • 如果栈顶元素是对应的左括号,则将栈顶元素弹出。
  2. 最后检查栈是否为空:扫描结束后,栈为空说明所有的括号都成功匹配;如果栈不为空,说明有未匹配的左括号,返回false

此外,通过栈可以有效地解决问题中的回溯问题,如深度优先搜索。

队列(Queue)

队列是一种先进先出的线性数据结构。队列的操作发生在两端,通常有如下主要操作:

  1. push() :在队尾插入一个元素。时间复杂度:O(1)。
  2. pop(): 删除队列第一个元素。时间复杂度:O(1)。
  3. size(): 返回队列中元素个数。时间复杂度:O(1)。
  4. empty(): 如果队列空则返回true。时间复杂度:O(1)。
  5. front(): 返回队列中的第一个元素。时间复杂度:O(1)。
  6. back(): 返回队列中最后一个元素。时间复杂度:O(1)。

队列常用于任务调度(174题)广度优先搜索等场景,可以通过数组或链表实现,也可以通过循环队列来优化性能,避免浪费空间。

总结

栈和队列作为两种基础的线性数据结构,分别在不同类型的问题中扮演重要角色。通过理解其基本操作和应用场景,我们可以有效地选择合适的数据结构来解决问题。对于入门同学,建议通过手写代码实现栈和队列,并通过刷题来加深对这些数据结构的理解。同时,学习栈和队列时,尽量掌握其空间和时间复杂度的分析,以便更好地应对实际问题。