题目描述:
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
本题1、2的描述,结合后,我的理解是:左括号右括号必须成对出现,且不允许嵌套。比如:"()[]{}[]"表示正确、但"([)] "表示错误,因为(括号和)括号没连在一起。
该题在提交时,有一个用例"([)]"答案为错误,故我在此提示。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = "()" 输出:true
示例 2:
输入:s = "()[]{}" 输出:true
示例 3:
输入:s = "(]" 输出:false
提示:
1 <= s.length <= 104
s
仅由括号'()[]{}'
组成
解题准备:
1.了解栈:栈是一种线性的数据结构,可以理解为一个特殊数组(或链表),仅提供两个基础操作:入栈和出栈,并且具有先进后出的性质【可以看成只有一个口,同时作为出口、入口的结构】(类似于配对、转变字符串顺序、状态模式undo等,都可以用栈来做)【在此作为了解,我会在后面展示为什么要用栈,其实可以用其它结构】
2.了解本题涉及的基础操作:既然要判断s是否为有效字符串,应该要遍历s,所以涉及查找;涉及栈,故涉及入栈、出栈,有增加和删除。。故此,有增删查
3.模拟本题操作:本题操作好像有点无从下手,如果是人眼看,一下子就明白了。但是机器是分步骤的,所以有点奇怪,在此先不说。
解题难点分析:
本题难点,在于如何把一眼看出来的模糊,转换为一步步迭代的清晰
思考步骤:
1.假设字符串为")([]",人怎么做?
显然,开头就遇到),所以直接返回false。
2.进阶一点,如果是"()[])(",人怎么做?
显然,虽然前面四个字符都匹配,但是)(也犯了1的错误,返回false。
3.好像有点意思了,能不能分步来做?
我们知道,一个字符串,本质就是一个字符数组,能不能从数组的角度来理解?
4.对于字符数组,怎么理解?
1st.我们发现,对于)([],从下标0开始,)前面不满足匹配,故返回false。
2nd.对于(),从下标0开始, (无法判断返回true或false,移至下标1,)应该找个匹配了,发现下标0恰好满足。又因为字符数组刚好遍历结束,所以应该返回true。
3rd.可是,如果字符串为())呢?虽然知道下标0、1满足,但是移动到下标2的字符')'时,找前面的字符,明显有下标0为'(',满足题意,返回true?明显错误。
4th.由3rd,我们应该建立一个机制,如果被遍历过的左括号,就不能再遍历,干脆用个boolean数组,不过最初,我们无法确定左括号数目,难道要用可变的数组?反正右括号如果遍历到了,也不再使用,干脆直接用个长度为s.length()的boolean数组,标志左、右括号的使用情况。
5th.那么,再面对()),此时由于下标0、1已经标志为true(或false),下标2的),明显不满足题意,返回false;
6th,对于"(){}()"、"()[)"同理了
如何转变为用栈解决:
我们明显可以看出,对于字符数组,遍历时,如果是左括号,不处理,右括号,则处理两个数据。
我们遍历字符数组(命名为charA),使用了boolean数组(命名为boolean)辅助,看起来还可以,深入思考一下,比如字符串"([{}])"。
1.明显,在遍历0、1、2时,对于下标0、1、2,无法使其为true。
2.面对下标3,由于我们没保存数组情况(实际保存了也得遍历),所以得先遍历一次boolean,查找下标x<3的数据,是否存在false(未被使用)的数据,并且,如果未被使用,是否为{,===其实也可以发现,本题还存在可能出错。【所以,解决方法就是倒着查,从下标3-1=2的数据,一直查到=0的数据,第一个false的boolean,是否为{,是则OK,不是则返回false】
3.假设步骤2完美,到了下标为4的字符,又要从3开始遍历(使用【】中的方法),一直到下标为1,才找到。
4.同理。
所以,明显可以看到,boolean数组在解决问题的同时,存在大量重复运算。
既然已经被使用过的数据,为什么一而再再而三的遍历呢?
如果我们有一个数组A,其长度随时随着存储的数据量的变化而变化,比如此时A中有3个元素,其长度就为3,此时A有5个元素,长度就为5。
那多好?遇到左括号,把它添加到A中,遇到右括号,把对应的左括号从A中删除。
这时候,我们只需要判断两件事,1.遍历结束后,A中是否残留元素。
2.当遇到右括号时,A是否有数据?如果有,是否匹配?【右括号来临,可以直接查看A.length -1,如果不匹配,返回false即可】
由此,我们发现,普通的数组很难处理,但栈确实很满足这些要求,所以用栈。【栈的长度不可变,但是其元素长度却被标记,可以认为可变,遇到左括号入栈,右括号pop一个栈的元素进行比较,只有两种结果,要不不匹配返回false,要不匹配遍历charA的下一个元素】
代码:
时间复杂度大约是O(n) ,n为字符串长度
class Solution {
public boolean isValid(String s) {
Stack<Character> data=new Stack<>();
for(char temp:s.toCharArray()){
if(temp=='('||temp=='['||temp=='{'){
data.push(temp);
}else{
if(data.isEmpty()){
return false;
}
if(!fuzhu(data, temp)){
return false;
}
}
}
if(!data.isEmpty()){
return false;
}
return true;
}
private boolean fuzhu(Stack<Character> data, char k){
char var;
if(k==')'){
var=data.pop();
if(var=='('){
return true;
}
return false;
}else if(k=='}'){
var=data.pop();
if(var=='{'){
return true;
}
return false;
}else{
var=data.pop();
if(var=='['){
return true;
}
return false;
}
}
}
以上内容即我想分享的关于力扣热题9的一些知识。
我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。