在使用豆包MarsCode AI刷题过程中,我对栈和队列这两个数据结构进行了深入的总结与分析。
栈(Stack)
栈是一种后进先出的线性数据结构。栈只允许在一端进行操作,即栈顶。常见的栈操作包括:
- push() :将元素添加到栈顶。时间复杂度:O(1)。
- pop() :移除栈顶元素,并返回该元素。时间复杂度:O(1)。
- empty() :检查栈是否为空。时间复杂度:O(1)。
- top():返回栈顶元素。时间复杂度:O(1)。
栈的应用场景非常广泛,特别是在表达式求值,如52题、168题。
逆波兰式表达式求值如下:
-
中缀表达式转后缀表达式(逆波兰表达式) :首先将中缀表达式转换为后缀表达式,利用栈处理运算符的优先级和括号嵌套。这样,操作数和运算符的顺序能够满足后缀表达式计算的需求。
-
运算符优先级:加法和减法的优先级较低,乘法和除法的优先级较高。括号内的运算需要优先计算。
-
转换规则:
-
如果遇到一个数字,直接输出。
-
如果遇到一个运算符,根据运算符的优先级与栈顶运算符的优先级进行比较:
- 如果当前运算符优先级高于栈顶运算符,压栈。
- 如果当前运算符优先级低于或等于栈顶运算符,弹出栈顶运算符并输出,直到栈顶运算符的优先级低于当前运算符。
-
如果遇到左括号,直接压栈。
-
如果遇到右括号,弹出栈内运算符直到遇到左括号。
-
-
-
计算后缀表达式:一旦中缀表达式被转化为后缀表达式,就可以用栈进行计算了。
-
逐个扫描后缀表达式的元素:
- 如果是操作数,直接入栈。
- 如果是运算符,从栈中弹出两个操作数,进行运算,将结果压栈。
-
最后栈中会剩下一个元素,即运算结果。
-
括号匹配思路如下:
-
扫描表达式:逐个扫描表达式的字符。
-
如果遇到左括号
(、[、{,则将其压栈。 -
如果遇到右括号
)、]、},则需要检查栈顶元素是否为对应的左括号:- 如果栈为空或栈顶元素不是对应的左括号,则表示括号不匹配,返回
false。 - 如果栈顶元素是对应的左括号,则将栈顶元素弹出。
- 如果栈为空或栈顶元素不是对应的左括号,则表示括号不匹配,返回
-
-
最后检查栈是否为空:扫描结束后,栈为空说明所有的括号都成功匹配;如果栈不为空,说明有未匹配的左括号,返回
false。
此外,通过栈可以有效地解决问题中的回溯问题,如深度优先搜索。
队列(Queue)
队列是一种先进先出的线性数据结构。队列的操作发生在两端,通常有如下主要操作:
- push() :在队尾插入一个元素。时间复杂度:O(1)。
- pop(): 删除队列第一个元素。时间复杂度:O(1)。
- size(): 返回队列中元素个数。时间复杂度:O(1)。
- empty(): 如果队列空则返回true。时间复杂度:O(1)。
- front(): 返回队列中的第一个元素。时间复杂度:O(1)。
- back(): 返回队列中最后一个元素。时间复杂度:O(1)。
队列常用于任务调度(174题)、广度优先搜索等场景,可以通过数组或链表实现,也可以通过循环队列来优化性能,避免浪费空间。
总结
栈和队列作为两种基础的线性数据结构,分别在不同类型的问题中扮演重要角色。通过理解其基本操作和应用场景,我们可以有效地选择合适的数据结构来解决问题。对于入门同学,建议通过手写代码实现栈和队列,并通过刷题来加深对这些数据结构的理解。同时,学习栈和队列时,尽量掌握其空间和时间复杂度的分析,以便更好地应对实际问题。