题目描述
思路解析动画文字版
核心一句话:栈存「等待配对的左括号下标」。右括号空栈即删,扫完栈中残留即删。记住这套,下面每帧都在套它。
开局:指针还没进入字符串,栈是空的。橙色将标记「正在看」的字符,左边竖栏是栈(底→顶)。
先扫一眼 s="a(b)c)d(":里面有 2 个左括号、2 个右括号。它们能否两两配上,得用栈一位位走才知道。从第 0 位开始。
看第 0 位「a」。先判断它是不是括号,结论是字母,那它和括号配对无关。
字母「a」直接保留、完全不碰栈。栈里仍是 0 个等待配对的左括号,继续往后走。
看第 1 位「(」。左括号先不下结论,把它的下标 1 压进栈,排队等一个右括号来配对。
下标 1 入栈,现在栈顶是它。只要后面来个右括号,这一对就能配上。
看第 2 位「b」。先判断它是不是括号,结论是字母,那它和括号配对无关。
字母「b」直接保留、完全不碰栈。栈里仍是 1 个等待配对的左括号,继续往后走。
看第 3 位「)」。栈不空,栈顶是下标 1 的左括号,正好和它配成一对。
配对成功!弹出栈顶下标 1。位置 1 的「(」和位置 3 的「)」都是合法的,保留。
看第 4 位「c」。先判断它是不是括号,结论是字母,那它和括号配对无关。
字母「c」直接保留、完全不碰栈。栈里仍是 0 个等待配对的左括号,继续往后走。
看第 5 位「)」。栈是空的,左边没有任何待配的左括号,这个右括号是多余的,标记删除。
下标 5 进入「待删」名单。这是第一类坏括号:右括号孤立无援。继续往后扫。
看第 6 位「d」。先判断它是不是括号,结论是字母,那它和括号配对无关。
字母「d」直接保留、完全不碰栈。栈里仍是 0 个等待配对的左括号,继续往后走。
看第 7 位「(」。左括号先不下结论,把它的下标 7 压进栈,排队等一个右括号来配对。
下标 7 入栈,现在栈顶是它。只要后面来个右括号,这一对就能配上。
整串扫完。栈里若还有元素,它们都是「等不到右括号」的左括号,属于第二类坏括号,下面逐个清掉。
栈顶下标 7 的「(」始终没等来右括号,标记删除。栈剩 0 个,继续清。
栈清空了,待删名单确定为下标 { 5、7 }。把没被标记的字符按原顺序拼起来,就是答案。
跳过下标 5、7,剩下的字符拼成 "a(b)cd",所有括号都配对合法,且只删了最少的 2 个。
边界先想清:无括号原样、单括号必删、全坏删光。
两个高频追问:栈法 vs 计数法、以及解的唯一性。
参考代码
class Solution: def minRemoveToMakeValid(self, s: str) -> str: remove = set() stack = [] for i, ch in enumerate(s): if ch == '(': stack.append(i) elif ch == ')': if stack: stack.pop() else: remove.add(i) remove.update(stack) return ''.join(ch for i, ch in enumerate(s) if i not in remove)复杂度
- 时间:O(n),扫一遍标记 + 再拼一遍结果
- 空间:O(n),栈和删除标记最多记 n 个下标
易错点
面试追问把动画讲成自己的话
追问能不能不用栈,只用一个计数器?
追问题目说「返回任意一个合法结果」,会有多解吗?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
子数组最小乘积的最大值
LeetCode 1856 · 中等 · 沿着 栈套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题