引言
大家好啊,我是前端拿破轮😁。
跟着卡哥学算法有一段时间了,通过代码随想录的学习,受益匪浅,首先向卡哥致敬🫡。
但是在学习过程中我也发现了一些问题,很多当时理解了并且AC的题目过一段时间就又忘记了,或者不能完美的写出来。根据费曼学习法,光有输入的知识掌握的是不够牢靠的,所以我决定按照代码随想录的顺序,输出自己的刷题总结和思考。同时,由于以前学习过程使用的是JavaScript
,而在2025年的今天,TypeScript
几乎成了必备项,所以本专题内容也将使用TypeScript
,来巩固自己的TypeScript
语言能力。
题目信息
有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左括号。
题目分析
本题是栈的应用的最经典的题目。有效括号的匹配,对于熟悉栈的同学来说此题的解法并不难。本文想要分析的关键是,为什么别人一看到这个题就会想到使用栈这种数据结构?为什么我们就想不到呢?很多同学可能会说,别人刷到过这个题呗。这确实是一个原因,而且是很重要的原因。当然也有可能根据leetcode的题目分类,这道题目被划归到栈里面,当然要使用栈了。这些都是原因,但是不是本质原因。从根本上来说,是因为本题题目中的特征符合栈后进先出(LIFO)的特点。括号进行匹配的时候,最后出现的左括号,却要最先匹配右括号,这不就是典型的栈的特征吗。所以掌握了根本原因后,以后我们在遇到没有见过的题目时,才能判断出解题方法。
本题整体上的思路就是,从左往右遍历括号字符串,如果是左括号,则入栈,如果是右括号,则将栈顶元素弹出,判断两者是否匹配。如果不匹配,直接剪枝返回false。如果匹配,则接着往后扫描。直到最后,判断辅助栈是否为空。如果栈非空,则说明有多余的左括号,不匹配。否则匹配。
这里面除了整体上使用栈的思路外,还有两个点需要注意。
-
如何判断一个字符串是左括号还是有括号
-
如何判断两个括号是否匹配
对于1,简单做法可以直接用相等符===
和逻辑运算符||
来进行判断。比如if(c === '{' || c === '[' || c === '(')
来进行判断,也可以写一个Set
哈希表来判断,比如const set = new Set('{', '[', '(')
,然后利用APIset.has()
来判断,如果在set中,就是左括号。由于题目说到整个字符串只有左右括号,所以如果不在set中,一定是右括号。
对于2,可以针对不同的情况分别进行判断,比如if(c === '}')
的情况下再判断栈顶弹出的元素是不是}
,但是这样就会导致if-else
嵌套过深,导致圈复杂度过高,可读性也会降低。另一种方式是使用一个对象,会二维数组,保存左右括号之间的对应关系。
最合理的方案是使用哈希表Map
,可以同时解决1和2的问题。因为Map
拥有Set
的功能,而且还能存储键值对,直接一步解决了两个问题。具体可以看题解部分。
题解
function isValid(s: string): boolean {
// map对象存储映射关系
const map = new Map([
['}', '{'],
[']', '['],
[')', '(']
]);
// 辅助栈
const stack = [];
// 遍历字符串
for (let c of s) {
// 如果不在map中,一定是左括号,直接入栈
if (!map.has(c)) {
stack.push(c);
} else {
// 如果c在map中,说明c是右括号,将栈顶元素弹出
const left = stack.pop();
// 判断栈顶元素是不是当前右括号匹配的左括号,不匹配直接剪枝
if (left !== map.get(c)) return false;
}
}
// 最后结束后要根据栈是否为空
return stack.length === 0;
};
时间复杂度: , 只需要遍历一次字符串。
空间复杂度: ,需要问题规模的辅助栈,还有常数级别的map对象空间。总体是。
总结
本题是栈最经典的题目,因为括号匹配的后进先出的特点和栈完美吻合。在处理过程中还要用到map对象来存储映射关系。以及最后一定要记得判断辅助栈是否为空。这一点很容易被忽略。很多同学遍历完发现都匹配就直接返回true
,没有考虑到左括号可能多余的情况。
好了,这篇文章就到这里啦,如果对您有所帮助,欢迎点赞,收藏,分享👍👍👍。您的认可是我更新的最大动力。由于笔者水平有限,难免有疏漏不足之处,欢迎各位大佬评论区指正。
往期推荐✨✨✨
我是前端拿破轮,关注我,一起学习前端知识,我们下期见!