960 字
5 分钟
删除无效的括号

题目描述
给你一个由若干括号和字母组成的字符串 s
,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
解题思路
- 最小删除量计算:遍历字符串,统计未匹配的左右括号数量
lremove
/rremove
(需删除的最少括号数)。 - 回溯搜索:从起始位置遍历字符串,递归尝试删除每个无效括号:
- 跳过连续重复括号避免重复解
- 剩余字符不足时剪枝
- 删除左括号或右括号后更新对应删除量
- 结果验证:当删除量归零时,用栈验证括号有效性:
- 遇左括号计数+1,遇右括号计数-1
- 中途出现负值立即终止
- 最终计数为零则为有效解
关键洞察
- 删除量确定性:最小删除量
(lremove,rremove)
是固定值,直接作为搜索边界。 - 重复括号优化:跳过连续相同括号(
str[i] === str[i-1]
),避免生成重复解。 - 双效剪枝:
- 资源剪枝:剩余字符数
< lremove+rremove
时终止 - 逻辑剪枝:优先删除无效括号缩小搜索空间
- 资源剪枝:剩余字符数
- 延迟验证:仅在删除完成时检查有效性,减少中间状态验证开销。
复杂度分析
指标 | 说明 |
---|---|
时间复杂度 | O(n·2^n):最坏情况每个括号有删/留选择(n≤25 实际剪枝高效) |
空间复杂度 | O(n²):递归深度 O(n),每层 substr 创建新字符串(O(n)) |
验证复杂度 | O(n):isValid 单次遍历,仅终点调用 |
注:剪枝策略使实际性能远优于理论最坏复杂度,字符串约束(n≤25)保证可行性。
代码实现
递归版本
/** * @link {https://leetcode.cn/problems/remove-invalid-parentheses} * @param {string} s * @return {string[]} */var removeInvalidParentheses = function(s) { const helper = (str, start, lremove, rremove) => { if (lremove === 0 && rremove === 0) { if (isValid(str)) { res.push(str); } return; } for (let i = start; i < str.length; i++) { if (i !== start && str[i] === str[i - 1]) { continue; } // 如果剩余的字符无法满足去掉的数量要求,直接返回 if (lremove + rremove > str.length - i) { return; } // 尝试去掉一个左括号 if (lremove > 0 && str[i] === '(') { helper(str.substr(0, i) + str.substr(i + 1), i, lremove - 1, rremove); } // 尝试去掉一个右括号 if (rremove > 0 && str[i] === ')') { helper(str.substr(0, i) + str.substr(i + 1), i, lremove, rremove - 1); } } } const res = []; let lremove = 0; let rremove = 0; for (const c of s) { if (c === '(') { lremove++; } else if (c === ')') { if (lremove === 0) { rremove++; } else { lremove--; } } } helper(s, 0, lremove, rremove); return res;}const isValid = (str) => { let cnt = 0;
for (let i = 0; i < str.length; i++) { if (str[i] === '(') { cnt++; } else if (str[i] === ')') { cnt--; if (cnt < 0) { return false; } } } return cnt === 0;}
迭代版本
const removeInvalidParentheses = (s) => { const isValid = (str) => { let balance = 0; for (const c of str) { if (c === '(') balance++; else if (c === ')') balance--; if (balance < 0) return false; } return balance === 0; };
let queue = new Set([s]); while (queue.size) { const nextQueue = new Set(); const validResults = [];
for (const expr of queue) { if (isValid(expr)) { validResults.push(expr); } // 找到有效表达式后停止生成下一层 if (validResults.length) continue;
// 生成所有删除一个括号的表达式 for (let i = 0; i < expr.length; i++) { if (expr[i] !== '(' && expr[i] !== ')') continue; const newExpr = expr.slice(0, i) + expr.slice(i + 1); nextQueue.add(newExpr); } }
if (validResults.length) return validResults; queue = nextQueue; } return [""];};
实际应用场景
- 公式编辑器: 自动修复数学公式中的括号不匹配`
- SQL解析器: 修复嵌套查询中的括号错误`
- 编译器前端: 处理代码中的括号不匹配错误`
- 交互式教程: 括号匹配的编程教学工具`