题目描述
给定一个只包括 '(',')','{','}','[',']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
示例 4:
输入:s = "([)]"
输出:false
示例 5:
输入:s = "{[]}"
输出:true
力扣原题目地址:https://leetcode.cn/problems/...
思路分析
阅读了以上题目的要求以后,我们大致可以明白一下几点:
- 首先,括号是成对出现的,有左括号,就有对应的右括号,所以括号字符串的长度一定是2的倍数即偶数,才符合基本条件,即:
s.length % 2 === 0
取模2等于1就是奇数了,肯定是不行的 当出现一个括号的时候,在未来的某一时刻,会出现另一个对的括号,与他成双成对。所以咱们也可以换种方式理解,更有助于我们解题
换种方式理解
这个比喻不太恰当,不过有助于理解题目
仓库(字符串)
有一堆鞋子(括号)
,有大人的鞋子(大括号)
、和小孩的鞋子(小括号)
,鞋子嘛,有左脚和右脚(左括号和右括号)
。老板让咱给鞋子做左脚鞋和右脚鞋配对
这里得加个条件:通过左鞋可以匹配到对应右鞋,反之不行。咱要是问啥?因为括号匹配只能是先左后右,没有先右括号,后左括号的
于是我们遍历
这个仓库,一只一只把鞋子拿出来。用笔在一张纸
上记下当前这个鞋子需要匹配的另外的鞋子是啥型号的(纸张记下匹配需求)
,记录好以后,我们就把这张纸,放在一个不用的鞋盒子里(入栈)
,如果一直记录下去,鞋盒子里的纸会有很多张,甚至鞋盒子装不下了(栈溢出)
,所以我们需要对比看看后续拿到的鞋子,是不是这个鞋盒子里上面哪张纸上写的需要的鞋子(看看能不能匹配上),能匹配上,就把鞋盒子里这张纸,拿出来(出栈),说明匹配成功;
匹配不上,就把继续抽张纸,写上手里的这个鞋子,需求的另一只鞋子是啥型号的(继续入栈)
,如此操作,直至操作完。
操作完以后,再看看鞋盒子里有没有纸张,有的话,说明有鞋子没匹配上。没有纸张,就说明都匹配上了
大家发挥自己的想象力,设想一下这种场景,,这个场景理解了的话,再看下方的代码,就很好理解了
使用栈数组去解决
var isValid = function (s) {
// 1. 如果是奇数个,肯定是不符合条件的,因为括号是成对成对出现的
if (s.length % 2 === 1) {
return false
}
// 2. 如果是偶数个,再进一步做判断,看是否符合
let stackArr = [] // 3. 定义一个栈(数组)用来存放配对需要的标识
for (let i = 0; i < s.length; i++) { // 4. 遍历拿到字符串中每一个括号
// 5. 一开始栈数组是空的,所以不用对比,继续往下看
if (stackArr[stackArr.length - 1] === s[i]) {
stackArr.pop()
}
// 6. 接下来根据遇到的是什么括号,往栈数组中入栈所需要的另一半括号
else {
if (s[i] === '(') { // 7. 如果是左小括号,就在栈数组中追加一个标记,需要右小括号,进行搭配
stackArr.push(')')
} else if (s[i] === '[') { // 8. 如果是左中括号,就在栈数组中追加一个标记,需要右中括号,进行搭配
stackArr.push(']')
} else if (s[i] === '{') { // 9. 如果是左大括号,就在栈数组中追加一个标记,需要右大括号,进行搭配
stackArr.push('}')
} else { // 10. 如果直接就直接遇到了右小括号、右中括号、右大括号,那就说明是有问题的,因为只能是左匹配右,不能右匹配左
return false // 11. 比如咱们正常使用括号() [] {} 从来不会这样用 )( ][ }{
// 12. 加之上方的if判断,是不能出现这种情况的,如果出现了,就说明括号字符串是有问题的,直接false即可
}
}
}
// 13. 匹配完了以后,看看是否有没匹配上的
if (stackArr.length == 0) { // 14. 要是都匹配上了,说明就是没问题的,返回true
return true
} else { // 15. 反之,说明不符合
return false
}
};
使用Map集合做代码精简
可以理解为使用Map集合创建一个需求匹配字典
var isValid = function (s) {
if (s.length % 2 === 1) {
return false
}
let map = new Map() // 创建一个map集合
map.set('(', ')') // 左小括号匹配右小括号
map.set('[', ']') // 左中括号匹配右中括号
map.set('{', '}') // 左大括号匹配右大括号
let stackArr = []
for (let i = 0; i < s.length; i++) {
if (stackArr[stackArr.length - 1] === s[i]) {
stackArr.pop()
}
else {
if (map.has(s[i])) { // 若当下的这个半拉括号在map集合中有,即要么( 要么[ 要么{
let need = map.get(s[i]) // 那就拿到对应所需要的另一半的值,要么) 要么] 要么}
stackArr.push(need) // 然后把入栈告知需要的另一半括号是啥,等待匹配之
} else { // 如果map集合中没有,说明就是遇到了) 或] 或},那就是有问题的,所以直接返回false
return false
}
}
}
if (stackArr.length == 0) {
return true
} else {
return false
}
};
js中关于栈的简单理解
我们知道数组比较灵活,可以头部追加元素unshift
、头部删除元素shift
、尾部追加元素push
、尾部删除元素pop
,或者直接splice
想怎么操作就怎么操作。但是栈不行。
可以想象一下,数组平常是横着的,而栈就是竖着的,立起来的
。所以只有上方一个入口了,追加元素或者删除元素,都只能通过上方的一个入口,进行操作。
栈是被限制的数组,也可以称之为栈数组
也就是说,栈数组只能push尾部追加
和pop尾部删除
,即先追加进来的元素,只能后删除;后追加进来的元素,可以先删除。即:先进后出,或后进先出
至于队列,其实也是被限制的数组,也可以称之为队列数组,篇幅原因,在此不赘述
最后抛出一个观点,咱们做算法题的时候,不一定非要和leetcode官方的解法,一模一样。只要思路一致即可,毕竟万变不离其宗,思路很重要哦^_^
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。