「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
今天说一下如何验证有效的括号,LeetCode 20
栈是一个十分简单的数据结构,只有两个操作,入栈和出栈。后进者先出,先进者后出,这就是栈的结构。
虽然栈的用途范围比较小,但是这个问题用栈的思路考虑是最合适的。
实现过程
左右两边必须是相同类型的括号,然后必须是顺序闭合的。
不管这三种类型的括号怎样排列,只要是符合闭合规则的,必然是先有左括号,后有右括号。如果是一个符合规则的闭合括号字符串,那么不管任何一组闭合括号的中间有多少括号,中间必然会有这三种形式 (),{},[] 作为中间的转折,开始一直是左括号,遇到刚才三种形式后,开始变成右括号。
如果把它们从中间分割开来,就可以发现它们都是一一对称的。
我们根据栈的思想,遇到左括号就进栈,遇到右括号就出栈并且校验,直到最后都抵消为空。这样就可以得到一个符合规则的闭合括号字符串。
下面是完整的代码:
第189-192行代码,判断字符串长度,长度如果小于等于 1 或者是单数,因为闭合括号必然是双数,那么长度为单数就不符合规则,直接返回错误。
第194-198行代码,定义一个数组,右括号为键,左括号为值。可以根据右括号找到对应的左括号。
第200-201行代码,定义一个数组,表示栈结构。$flag 表示记录在栈顶的位置,需要随时出栈的元素。也可以每次都使用 count函数计算下数组长度,这样逻辑简单,不用考虑元素出栈后的键变化。
第202-214行代码,根据字符串的长度,遍历字符串。
第203-205行代码,由于肯定是先出现左括号,所以我们需要先把遇到的左括号入栈。遇到三种类型的左括号(,{,[ 。先把$flag的值加一,然后根据$flag,把单个字符放入数组内。
第207-209行代码,遇到右括号的处理方法。如果遇到右括号时,数组$arr内没有数据,说明字符串时不符合规则的,因为有右括号,就必然有左括号。
然后我们需要判断当前遇到的右括号是否能和在栈顶的括号组合成要给闭合括号。我们通过上面定义的$map数组,根据此时需要判断的右括号的值$s[$i],可以得到对用的左括号的类型。最后和栈顶的括号$arr[$flag]做比较。如果一致的话,就可以继续遍历。如果不一致,说明字符串不是闭合的。直接返回错误。
此时如果判断一致,就是上面说的,括号以这三种形式
(),{},[]出现的时候。
第211-212行代码,通过判断括号可以闭合,那么就需要把栈顶的括号去掉,下次需要判断栈顶的下一个元素括号是否闭合。因为数组内去掉了一个元素,所以键的值$flag需要减一。
第216行代码,这样写可以理解比较麻烦,如果写成这样是不是好理解了。
if(!empty($arr)){
return false;
}
return true;
如果遍历完字符串后,没有发现不符合闭合规则的括号,那么最后需要判断下数组内,也就是栈内是否还有元素,如果有的话,说明还是有没有闭合的括号,因此返回错误。否则的话返回成功,这是一个符合规则的闭合括号字符串。
执行结果
一些情况分析,为什么加这样的代码
上图中的207-209行代码,通过$map数组映射对应的括号,也可以写成下面这种好理解的。
if($s[$i] == ')' && $arr[$flag] != '('){
return false;
}
if($s[$i] == ']' && $arr[$flag] != '['){
return false;
}
if($s[$i] == '}' && $arr[$flag] != '{'){
return false;
}
我们还判断了
count($arr) == 0
因为如果字符串是这样的话())(,当判断到第三个字符时,由于前面两个字符是闭合的括号,所以$arr数组内一已经没有数据了,此时遍历到右括号,说明字符串是不符合规则的。
以上就是有效的括号验证思路。