LeetCode 第1249题:移除无效的括号,JavaScript解决方案与经验总结
2023-10-31 00:04:05
移除无效括号:掌握字符串、数据结构和算法的艺术
在算法工程师的世界中,解决棘手的 LeetCode 问题是展示技能和知识的绝佳方式。LeetCode 第 1249 题“移除无效的括号”就是这样一道经典题目,它不仅考验你的字符串操作能力,还考验你对数据结构和算法的理解。
算法原理
乍一看,这道题似乎很简单。给定一个由左括号和右括号组成的字符串,你的任务是移除无效的括号,得到最长的有效括号子字符串。然而,真正解决起来却并不容易。
这道题的算法原理融合了贪心算法和栈数据结构。贪心算法帮你确定要删除的左括号,而栈则用于跟踪有效的括号对。
- 贪心策略: 从左到右遍历字符串。每当遇到一个左括号,就将其压入栈中。当遇到一个右括号时,检查栈顶是否为左括号。如果是,则弹出栈顶元素,表示你找到了一个有效的括号对。如果没有,则该右括号无效,需要删除。
- 栈的使用: 栈用于存储有效的左括号。如果栈为空,则表明你遇到了一个孤立的右括号,需要将其标记为无效。
代码实现
了解了算法原理后,让我们看看如何用 JavaScript 编写代码:
function longestValidParentheses(s) {
const stack = [];
const invalidLeft = [];
for (let i = 0; i < s.length; i++) {
if (s[i] === '(') {
stack.push(i);
} else {
if (stack.length > 0) {
stack.pop();
} else {
invalidLeft.push(i);
}
}
}
let result = '';
for (let i = 0, j = 0; i < s.length; i++) {
if (i === invalidLeft[j]) {
j++;
} else {
result += s[i];
}
}
return result.length;
}
代码优化:动态规划的魅力
尽管上述代码可以解决问题,但我们还可以利用动态规划(DP)进行优化。DP 的核心思想是将复杂问题分解成更小的子问题,然后逐一求解。
DP 数组 dp
中的每个元素存储了以该字符结尾的最长有效括号子串的长度。
- 如果当前字符是左括号,则
dp[i]
等于dp[i-1]
。 - 如果当前字符是右括号,则:
- 如果前一个字符是左括号,则
dp[i]
等于dp[i-2]
加 2。 - 如果前一个字符不是左括号,但前前一个字符是左括号,则
dp[i]
等于dp[i-1]
加dp[i-dp[i-1]-2]
加 2。
- 如果前一个字符是左括号,则
时间复杂度分析
使用栈的解法时间复杂度为 O(n),其中 n 是字符串的长度。使用 DP 的解法时间复杂度也为 O(n)。
注意细节
- 删除无效右括号时,一定要检查栈是否为空。
- 从左到右遍历字符串,因为无效左括号可能出现在任何位置。
- 初始化 DP 数组时,
dp[0]
为 0,因为字符串的第一个字符不可能是右括号。
结论
LeetCode 第 1249 题“移除无效的括号”是一道富有挑战性的算法题,它不仅考验你的编程技能,还考验你对算法原理的理解。通过结合贪心算法、栈数据结构和动态规划,你可以高效地解决这个问题。
常见问题解答
-
为什么需要用栈来跟踪有效的括号?
答:栈可以帮助你快速检查右括号是否与左括号匹配。如果栈为空,则表明右括号无效。 -
贪心算法是如何确定要删除的左括号的?
答:贪心算法从左到右遍历字符串。每当遇到一个没有匹配右括号的左括号,它都会标记为无效。 -
DP 解法如何优化代码?
答:DP 解法避免了不必要的计算。它存储了每个子字符串的有效长度,因此无需重复计算。 -
这道题的实际应用是什么?
答:这道题的实际应用包括编译器优化、文本编辑器和数据验证。 -
如何提高解决算法题的能力?
答:解决算法题的最佳方法是多练习。尝试不同的解法,分析代码的复杂度,并学习来自各种来源的算法原理。