栈相关题目:统一题解
栈是一种先进后出的数据结构,特别适用于需要追踪最近状态的场景。在编程中,栈常被用来解决括号匹配、重复字符消除等问题。下面我们通过几道经典的题目来深入了解如何利用栈来高效地解决问题。
题目1:火车驶入驶出顺序验证
题目描述
给定火车的进入顺序和期望的出站顺序,判断是否可以按照给定的出站顺序出站。这里的休息区结构类似于栈,即遵循先进后出的规则。
题解
我们使用一个辅助栈模拟火车的进入和出站过程:
- 遍历火车的进入顺序,对于每辆车,将其“驶入”栈。
- 每次“驶入”后,检查栈顶元素是否与出站顺序中的下一个元素相同。如果相同,栈顶出栈,代表该车出站。
- 重复上述步骤直到所有车完成操作。若最终栈为空,则表示可以满足出站顺序;否则无法满足。
这种模拟方法有效地利用栈的先进后出特性,使我们能在火车进入的过程中,动态判断和消除已经满足条件的出站顺序。时间复杂度为 (O(n)),空间复杂度为 (O(n)),其中 (n) 是火车的数量。
代码:
def solution(n: int, a: list, b: list) -> bool:
stack = []
j = 0 # b 的指针,指向下一个需要出栈的元素
for train in a:
stack.append(train) # 火车入栈
while stack and stack[-1] == b[j]:
stack.pop() # 出栈
j += 1 # 指向下一个出栈元素
return len(stack) == 0
题目2:括号字符串最短长度计算
题目描述
给定一个只包含 ( 和 ) 的括号字符串,要求我们通过合并相邻的 () 对,将字符串长度缩短到最小。
题解
我们用栈来追踪未匹配的括号对:
- 遍历字符串中的每个字符:
- 如果是
(,将其压入栈。 - 如果是
),则检查栈顶是否为(,如果是则弹出栈顶,这代表消除了一个匹配的括号对。
- 如果是
- 遍历结束后,栈中剩余的字符就是无法匹配的部分。栈的长度即为最终缩短后的字符串最短长度。
这种方法保证了时间复杂度为 (O(n)),空间复杂度也是 (O(n)),其中 (n) 是字符串长度。
代码:
def solution(s: str) -> int:
stack = []
for char in s:
if char == '(':
stack.append(char)
elif char == ')' and stack and stack[-1] == '(':
stack.pop() # 匹配一对括号
else:
stack.append(char) # 无法匹配的括号入栈
return len(stack)
题目3:相邻重复字母删除
题目描述
给定一个只包含小写字母的字符串,我们可以不断选择相邻的重复字母对进行删除,直到无法再删除为止。要求返回最终处理完所有重复项后的字符串。
题解
使用栈来存储非重复字符的结果:
- 遍历字符串的每个字符:
- 如果栈不为空且栈顶字符与当前字符相同,弹出栈顶字符,表示消除了相邻重复字符。
- 否则,将当前字符压入栈。
- 遍历结束后,栈中剩余的字符就是经过删除后的结果,按顺序拼接即可得到答案。
这种方法的时间复杂度为 (O(n)),因为每个字符最多被压入和弹出一次。空间复杂度为 (O(n))。
代码:
def solution(s: str) -> str:
stack = []
for c in s:
if stack and stack[-1] == c:
stack.pop() # 删除相邻的重复字符
else:
stack.append(c) # 将字符入栈
return ''.join(stack)
总结
在以上三道题目中,栈的主要作用是:
- 保存当前状态:在火车问题中,栈用于保存当前已进入但未出站的火车。
- 匹配与消除:在括号匹配和重复字符消除问题中,栈的先进后出特性帮助我们匹配并消除相邻的括号或重复字符。
- 最优结构操作:栈在处理具有前后依赖关系的问题时,提供了一种简单且有效的解法。
栈适合解决的典型问题包括括号匹配、重复字符消除、路径还原等,理解其工作原理并掌握基本的应用方式,可以极大提高代码的简洁性与效率。