Article / 文章
LeetCode 第301题:删除无效括号
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。 返回所有可能的结果。答案可以按 任意顺序 返回。 有效字符串需满足: - 左括号必须用相同数量的右括号闭合。 - 左括号必须在对应的右括号之前。 - 每个右括号都要有一个对应的左括号。
📖 文章摘要
本文详细解析LeetCode第301题“删除无效括号”,这是一道考察DFS和括号匹配的困难难度题目。文章提供了DFS和BFS两种实现方案,包含C#、Python、C++三种语言实现,配有详细的算法分析和性能对比。适合学习深度优先搜索和字符串处理的读者。
核心知识点: DFS、BFS、括号匹配、字符串处理
难度等级: 困难
推荐人群: 具备基础算法知识,想要提升搜索算法和字符串处理能力的开发者
题目描述
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
有效字符串需满足:
- 左括号必须用相同数量的右括号闭合。
- 左括号必须在对应的右括号之前。
- 每个右括号都要有一个对应的左括号。
示例
示例 1:
输入:s = "()())()"
输出:["(())()","()()()"]
示例 2:
输入:s = "(a)())()"
输出:["(a())()","(a)()()"]
示例 3:
输入:s = ")("
输出:[""]
提示
- 1 <= s.length <= 25
- s 由小写英文字母以及括号 ‘(’ 和 ‘)’ 组成
- s 中至多含 20 个括号
解题思路
本题可以使用两种方法来实现:
-
DFS(深度优先搜索):
- 统计需要删除的左右括号数量
- 递归尝试删除不同位置的括号
- 判断剩余字符串是否有效
- 时间复杂度O(2^n)
-
BFS(广度优先搜索):
- 逐层删除一个括号
- 检查每个生成的字符串是否有效
- 找到最短删除数量时的所有解
- 时间复杂度O(n * 2^n)
图解思路
DFS搜索过程分析表
| 步骤 | 操作 | 状态 | 说明 |
|---|---|---|---|
| 1 | 统计括号 | 计算需删除数量 | 找出多余的左右括号 |
| 2 | 选择删除位置 | 递归尝试 | 避免重复删除相同括号 |
| 3 | 验证有效性 | 检查结果 | 确保括号匹配正确 |
| 4 | 保存结果 | 添加到结果集 | 去重并保存有效解 |
括号匹配状态表
| 情况 | 左括号数 | 右括号数 | 处理方式 |
|---|---|---|---|
| 平衡 | n | n | 继续处理 |
| 左多 | n | < n | 删除左括号 |
| 右多 | n | > n | 删除右括号 |
| 无效 | < 右括号数 | m | 直接剪枝 |
代码实现
C# 实现
public class Solution {
private HashSet<string> result = new HashSet<string>();
public IList<string> RemoveInvalidParentheses(string s) {
int leftRemove = 0, rightRemove = 0;
// 统计需要删除的括号数量
foreach (char c in s) {
if (c == '(') {
leftRemove++;
} else if (c == ')') {
if (leftRemove > 0) {
leftRemove--;
} else {
rightRemove++;
}
}
}
// DFS搜索所有可能的解
DFS(s, 0, leftRemove, rightRemove, 0, new StringBuilder());
return result.Count > 0 ? result.ToList() : new List<string> { "" };
}
private void DFS(string s, int index, int leftRemove, int rightRemove, int leftCount, StringBuilder current) {
if (leftRemove < 0 || rightRemove < 0 || leftCount < 0) {
return;
}
if (index == s.Length) {
if (leftRemove == 0 && rightRemove == 0 && leftCount == 0) {
result.Add(current.ToString());
}
return;
}
char c = s[index];
int len = current.Length;
if (c == '(') {
// 删除左括号
if (leftRemove > 0) {
DFS(s, index + 1, leftRemove - 1, rightRemove, leftCount, current);
}
// 保留左括号
current.Append(c);
DFS(s, index + 1, leftRemove, rightRemove, leftCount + 1, current);
current.Length = len;
} else if (c == ')') {
// 删除右括号
if (rightRemove > 0) {
DFS(s, index + 1, leftRemove, rightRemove - 1, leftCount, current);
}
// 保留右括号
if (leftCount > 0) {
current.Append(c);
DFS(s, index + 1, leftRemove, rightRemove, leftCount - 1, current);
current.Length = len;
}
} else {
current.Append(c);
DFS(s, index + 1, leftRemove, rightRemove, leftCount, current);
current.Length = len;
}
}
}
Python 实现
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
def isValid(s: str) -> bool:
count = 0
for c in s:
if c == '(':
count += 1
elif c == ')':
count -= 1
if count < 0:
return False
return count == 0
level = {s}
while True:
valid = []
for item in level:
if isValid(item):
valid.append(item)
if valid:
return valid
next_level = set()
for item in level:
for i in range(len(item)):
if i > 0 and item[i] == item[i-1]:
continue
if item[i] in '()':
next_level.add(item[:i] + item[i+1:])
level = next_level
C++ 实现
class Solution {
private:
unordered_set<string> result;
bool isValid(const string& s) {
int count = 0;
for (char c : s) {
if (c == '(') count++;
else if (c == ')') count--;
if (count < 0) return false;
}
return count == 0;
}
public:
vector<string> removeInvalidParentheses(string s) {
// BFS实现
queue<string> q{{s}};
vector<string> ans;
unordered_set<string> visited{{s}};
bool found = false;
while (!q.empty() && !found) {
int size = q.size();
for (int i = 0; i < size; i++) {
string curr = q.front();
q.pop();
if (isValid(curr)) {
ans.push_back(curr);
found = true;
}
if (found) continue;
for (int j = 0; j < curr.length(); j++) {
if (curr[j] != '(' && curr[j] != ')') continue;
if (j > 0 && curr[j] == curr[j-1]) continue;
string next = curr.substr(0, j) + curr.substr(j + 1);
if (visited.count(next) == 0) {
visited.insert(next);
q.push(next);
}
}
}
}
return ans.empty() ? vector<string>{""} : ans;
}
};
执行结果
C# 实现
- 执行用时:96 ms
- 内存消耗:42.8 MB
Python 实现
- 执行用时:44 ms
- 内存消耗:15.1 MB
C++ 实现
- 执行用时:0 ms
- 内存消耗:7.2 MB
性能对比
| 语言 | 执行用时 | 内存消耗 | 特点 |
|---|---|---|---|
| C# | 96 ms | 42.8 MB | DFS实现,内存占用较大 |
| Python | 44 ms | 15.1 MB | BFS实现,性能适中 |
| C++ | 0 ms | 7.2 MB | BFS实现,性能最优 |
代码亮点
- 🎯 使用DFS和BFS两种实现方式
- 💡 巧妙处理括号匹配和剪枝
- 🔍 优化重复情况的处理
- 🎨 代码结构清晰,易于理解
常见错误分析
- 🚫 未处理重复括号情况
- 🚫 剪枝条件不完整
- 🚫 未正确统计需删除的括号数
- 🚫 未考虑空字符串情况
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| DFS | O(2^n) | O(n) | 实现简单,易于理解 | 可能有重复计算 |
| BFS | O(n * 2^n) | O(2^n) | 找到最优解,避免重复 | 空间消耗大 |
相关题目
- LeetCode 20. 有效的括号 - 简单
- LeetCode 22. 括号生成 - 中等
- LeetCode 32. 最长有效括号 - 困难
📖 系列导航
🔥 算法专题合集 - 查看完整合集
📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新第301题。
💬 互动交流
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
- 👍 点个赞,让更多人看到这篇文章
- 📁 收藏文章,方便后续查阅复习
- 🔔 关注作者,获取更多高质量算法题解
- 💭 评论区留言,分享你的解题思路或提出疑问
你的支持是我持续分享的动力!
💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!