代码随想录算法训练营进行到第十天,题目依次是232.用栈实现队列、225.用队列实现栈、20.有效的括号、1047.删除字符串中的所有相邻重复项。单看难度,这四道题不算狠,代码量也很小,但如果你跟着训练营一路刷过来,应该能感觉到这组题和前面数组、链表、哈希表的刷法不太一样——前两题是在用数据结构“造”另一个数据结构,后两题才是栈在具体场景里的直接应用。我见过不少同学抄完题解就算过,过两周再写232还是会卡在“什么时候把in栈的数据倒进out栈”这个细节上。
如果你也想彻底把这一组题吃透,我建议停下来认真复盘一次。文章不会只贴四个AC代码,而是重点解释每一题为什么这样设计、哪个细节最容易想当然、以及我在实际刷题中踩过和见别人反复踩的坑。这样等你自己遇到“栈和队列互相实现”的变形题时,才不会只是背模板。
1. 这四道题放在同一天,练的其实是同一个东西
1.1 为什么训练营要把“互相模拟”和“栈的应用”混在一起排
很多人拿到第十天的任务单,第一反应是:232和225是镜像题,20和1047又是独立的重复题,四道题放一起有点乱。其实编排逻辑很清晰。
前两道题考的是“容器语义模拟”,也就是你不能直接用现成的队列,却要用栈这种完全不同语义的结构来实现队列;或者反过来,用队列实现栈。后两道题考的是“栈的使用场景”,一类是括号匹配,一类是相邻重复项消除,它们不要求你自己实现新容器,而是把栈当作解题工具用进去。
如果拆开看,第一二题解决的是“栈和队列到底是什么”,第三四题解决的是“栈这个东西什么时候好用”。放到同一天,就是先让你把两种容器的行为差异彻底想明白,再让你用一个真实的算法题验证这种差异。所以我始终觉得,训练营把这几题安排在一起并不是随意拼凑,而是一套完整的“理解结构-封装结构-应用结构”的小闭环。
1.2 判断直觉:数据结构的作用是约束访问顺序
要真正做对这几道题,先建立一条最底层的直觉:栈和队列的差别,本质上是访问顺序的差别。
栈是后进先出(LIFO),你最后放进去的元素总是最先被取走;队列是先进先出(FIFO),谁先来谁先被处理。用生活场景类比的话,栈就像一摞盘子,你永远只能拿到最上面那个;队列就像食堂排队,前面的人打完饭离开了,后面的人才能往前挪。
这个差别听起来太基础了,但很多人做题时没有把它落到代码判断上。比如232题,看到题目后脑子里想的不是“我要让后进先出变成先进先出”,而是直接去背两个栈、一个负责入、一个负责出的模板。一旦题目换一个马甲,比如允许你用两个队列实现一个带最大值功能的栈,立刻就垮了。所以这一章先把访问顺序作为主语,后面四道题的代码才能被串起来理解。
1.3 四道题内部的递进关系
这一天的题目并不是简单并列,它们之间有明显的递进关系:
- 20和1047其实是最低层“栈的直接应用”:一个需要保存左括号顺序,一个需要保存当前已经处理的结果前缀,两者都用到了“栈顶就是最近状态”这个特性。
- 232是更高一层“栈的抽象应用”:光会用栈还不够,你得用两个具有相反方向栈的结构组合出队列的顺序。
- 225又是反向的抽象应用:你得把队列的“队尾”想法设法搬到“队头”,以匹配栈顶语义。
也就是说,如果你先把20和1047写顺了,
