这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "()[]{}" 输出:true
示例 3:
输入:s = "(]" 输出:false
示例 4:
输入:s = "([)]" 输出:false 示例 5:
输入:s = "{[]}" 输出:true
提示:
1 <= s.length <= 104 s 仅由括号 '()[]{}' 组成
解题
一、解题思路
解题的思考过程远比解法本身要重要。
所以为方便同学们能更易理解,下面将一步步解析思考得到的解题思路的过程。 题目要求字符串“有效”,而有效字符串的定义是:
1).对于没有闭合的左括号而言,越靠后的左括号,对应的右括号越靠前 2).满足后进先出,考虑用栈。
这种是抽象的定义,无法直接提供编码思路,我们需要把它转化为形式化语言,也就是“充要条件”。
所谓的“有效字符串”应当具有什么性质?
")"或者"())"肯定是不符合要求的,也就是说:(1)在任意位置,右括号的数量总是要小于等于对应的左括号的数量的。 "())"肯定也是不符合要求的,即:(2)左右括号最终的数量应当是相等的。 但是仅靠以上2点是充分条件吗?比如说"([)]"是满足我们的(1)(2)的,但它违背了定义2。
另一个方向去做有效思考会是怎么样的?
每一对括号都是相对应的,有左括号就必须配有右括号。所以当右括号出现时,左侧一定会有一个对应的左括号。 假定我们的括号有若干层嵌套,在最内层,右括号,假设为')',出现的时候,它的前一个字符,必须是'('。 我们可以通过反证法来证明这一点:如果'('与')'之间存在其他的括号,那么说明还有更深层的嵌套,这与我们的前提矛盾。 因此在最内层,一定会出现相连的"()"。 我们可以这样来思考,每一个右括号会消耗一个左括号,如果这对括号匹配完了,我们就可以将这对括号删除。 比如说原本的字符串是"[()]",删除中间的"()"之后,就变成了"[]",同样我们可以将这一对"[]"匹配。 类似的"[(){}]"也可以进行相似的处理。 这样我们就保证了“如果字符串s是一个有效字符串,而我们每次匹配成功都删除已匹配的括号,那么我们每次出现右括号时,都有一个相邻的相应左括号与之对应”。 比如字符串"{[()}]",我们匹配完"()"之后,变成了"{[}]",与'}'相邻的是'[',这个字符串也不符合定义。 而要完成这个匹配和消除的过程,最合适的数据结构就是栈了。
二、解题过程
1.新建一个栈,我们逐个处理字符串里的字符: 2.遍历字符串,如果是左括号,则入栈。 3.如果是右括号,类型不匹配就栈顶元素出栈,如果不是对应的左括号,则返回不合法,如果是,则继续执行。 4.直到遍历完全部的字符,如果栈为空,则说明每一个左括号都被消耗完了(匹配成功)说明该字符串符合要求,否则不合法。
三、代码实现
/**
* @param {string} s
* @return {boolean}
*/
var isValid = function(s) {
if(s.length % 2 === 1) return false
const stack = []
for(let i = 0; i < s.length; i++) {
const c = s[i]
if(c === '(' || c=== '{' || c === '[') {
stack.push(c)
}else {
let t = stack[stack.length - 1]
if(
(t === '(' && c === ')') ||
(t === '{' && c === '}') ||
(t === '[' && c === ']')
) {
stack.pop()
}else {
return false
}
}
}
return stack.length === 0
};